From 6634b92c611ad12da3a4395505ce4ecbc1e641ec Mon Sep 17 00:00:00 2001 From: Mushtaq Ali Date: Mon, 18 Sep 2017 16:40:57 +0500 Subject: [PATCH 1/5] Transcript organization credentials UI - EDUCATOR-1126 --- cms/djangoapps/contentstore/views/videos.py | 45 +- cms/static/js/factories/videos_index.js | 2 + .../views/active_video_upload_list_spec.js | 2 +- .../spec/views/course_video_settings_spec.js | 501 +++++++++++++++--- .../js/views/active_video_upload_list.js | 2 + cms/static/js/views/course_video_settings.js | 276 ++++++++-- cms/static/sass/views/_video-upload.scss | 43 +- ...s-update-org-credentials-footer.underscore | 4 + ...settings-update-settings-footer.underscore | 9 + .../js/course-video-settings.underscore | 47 +- ...se-video-transcript-preferences.underscore | 31 ++ ...video-transcript-provider-empty.underscore | 7 + ...eo-transcript-provider-selected.underscore | 8 + ...script-organization-credentials.underscore | 43 ++ cms/templates/videos_index.html | 1 + cms/urls.py | 2 + 16 files changed, 846 insertions(+), 177 deletions(-) create mode 100644 cms/templates/js/course-video-settings-update-org-credentials-footer.underscore create mode 100644 cms/templates/js/course-video-settings-update-settings-footer.underscore create mode 100644 cms/templates/js/course-video-transcript-preferences.underscore create mode 100644 cms/templates/js/course-video-transcript-provider-empty.underscore create mode 100644 cms/templates/js/course-video-transcript-provider-selected.underscore create mode 100644 cms/templates/js/transcript-organization-credentials.underscore diff --git a/cms/djangoapps/contentstore/views/videos.py b/cms/djangoapps/contentstore/views/videos.py index c7ef4be093..3f5b837809 100644 --- a/cms/djangoapps/contentstore/views/videos.py +++ b/cms/djangoapps/contentstore/views/videos.py @@ -31,6 +31,8 @@ from edxval.api import ( get_transcript_preferences, create_or_update_transcript_preferences, remove_transcript_preferences, + get_transcript_credentials_state_for_org, + update_transcript_credentials_state_for_org, ) from opaque_keys.edx.keys import CourseKey from openedx.core.djangoapps.video_config.models import VideoTranscriptEnabledFlag @@ -44,7 +46,13 @@ from util.json_request import JsonResponse, expect_json from .course import get_course_and_check_access -__all__ = ['videos_handler', 'video_encodings_download', 'video_images_handler', 'transcript_preferences_handler'] +__all__ = [ + 'videos_handler', + 'video_encodings_download', + 'video_images_handler', + 'transcript_preferences_handler', + 'transcript_credentials_handler' +] LOGGER = logging.getLogger(__name__) @@ -380,6 +388,32 @@ def transcript_preferences_handler(request, course_key_string): return JsonResponse() +@expect_json +@login_required +@require_POST +def transcript_credentials_handler(request, course_key_string): + """ + JSON view handler to post the transcript organization credentials. + + Arguments: + request: WSGI request object + course_key_string: string for course key + + Returns: An empty success response or 404 if transcript feature is not enabled + """ + course_key = CourseKey.from_string(course_key_string) + if not VideoTranscriptEnabledFlag.feature_enabled(course_key): + return HttpResponseNotFound() + + org = course_key.org + provider = request.json.get('provider') + + # TODO: Send organization credentials to edx-pipeline end point. + credentials = update_transcript_credentials_state_for_org(org, provider, exists=True) + + return JsonResponse() + + @login_required @require_GET def video_encodings_download(request, course_key_string): @@ -589,7 +623,8 @@ def videos_index_html(course): }, 'is_video_transcript_enabled': is_video_transcript_enabled, 'video_transcript_settings': None, - 'active_transcript_preferences': None + 'active_transcript_preferences': None, + 'transcript_credentials': None } if is_video_transcript_enabled: @@ -598,9 +633,15 @@ def videos_index_html(course): 'transcript_preferences_handler', unicode(course.id) ), + 'transcript_credentials_handler_url': reverse_course_url( + 'transcript_credentials_handler', + unicode(course.id) + ), 'transcription_plans': get_3rd_party_transcription_plans(), } context['active_transcript_preferences'] = get_transcript_preferences(unicode(course.id)) + # Cached state for transcript providers' credentials (org-specific) + context['transcript_credentials'] = get_transcript_credentials_state_for_org(course.id.org) return render_to_response('videos_index.html', context) diff --git a/cms/static/js/factories/videos_index.js b/cms/static/js/factories/videos_index.js index abbc23b952..1ad46cada1 100644 --- a/cms/static/js/factories/videos_index.js +++ b/cms/static/js/factories/videos_index.js @@ -15,6 +15,7 @@ define([ videoSupportedFileFormats, videoUploadMaxFileSizeInGB, activeTranscriptPreferences, + transcriptOrganizationCredentials, videoTranscriptSettings, isVideoTranscriptEnabled, videoImageSettings @@ -27,6 +28,7 @@ define([ videoUploadMaxFileSizeInGB: videoUploadMaxFileSizeInGB, videoImageSettings: videoImageSettings, activeTranscriptPreferences: activeTranscriptPreferences, + transcriptOrganizationCredentials: transcriptOrganizationCredentials, videoTranscriptSettings: videoTranscriptSettings, isVideoTranscriptEnabled: isVideoTranscriptEnabled, onFileUploadDone: function(activeVideos) { diff --git a/cms/static/js/spec/views/active_video_upload_list_spec.js b/cms/static/js/spec/views/active_video_upload_list_spec.js index 80fe73b87a..920100766f 100644 --- a/cms/static/js/spec/views/active_video_upload_list_spec.js +++ b/cms/static/js/spec/views/active_video_upload_list_spec.js @@ -43,7 +43,7 @@ define( activeTranscriptPreferences: {}, videoTranscriptSettings: { transcript_preferences_handler_url: '', - transcription_plans: {} + transcription_plans: null }, isVideoTranscriptEnabled: isVideoTranscriptEnabled }); diff --git a/cms/static/js/spec/views/course_video_settings_spec.js b/cms/static/js/spec/views/course_video_settings_spec.js index 6035e0b966..c44324c8e6 100644 --- a/cms/static/js/spec/views/course_video_settings_spec.js +++ b/cms/static/js/spec/views/course_video_settings_spec.js @@ -8,10 +8,19 @@ define( courseVideoSettingsView, renderCourseVideoSettingsView, destroyCourseVideoSettingsView, + verifyTranscriptPreferences, + verifyTranscriptPreferencesView, + verifyOrganizationCredentialsView, + verifyOrganizationCredentialField, + verifyMessage, verifyPreferanceErrorState, selectPreference, - chooseProvider, + verifyProviderList, + verifyProviderSelectedView, + resetProvider, + changeProvider, transcriptPreferencesUrl = '/transcript_preferences/course-v1:edX+DemoX+Demo_Course', + transcriptCredentialsHandlerUrl = '/transcript_credentials/course-v1:edX+DemoX+Demo_Course', activeTranscriptPreferences = { provider: 'Cielo24', cielo24_fidelity: 'PROFESSIONAL', @@ -21,6 +30,10 @@ define( preferred_languages: ['fr', 'en'], modified: '2017-08-27T12:28:17.421260Z' }, + transcriptOrganizationCredentials = { + Cielo24: true, + '3PlayMedia': true + }, transcriptionPlans = { '3PlayMedia': { languages: { @@ -72,15 +85,37 @@ define( }, display_name: 'Cielo24' } + }, + providers = { + none: { + key: 'none', + value: '', + displayName: 'N/A' + }, + Cielo24: { + key: 'Cielo24', + value: 'Cielo24', + displayName: 'Cielo24' + }, + '3PlayMedia': { + key: '3PlayMedia', + value: '3PlayMedia', + displayName: '3Play Media' + } }; - renderCourseVideoSettingsView = function(activeTranscriptPreferencesData, transcriptionPlansData) { + renderCourseVideoSettingsView = function(activeTranscriptPreferencesData, transcriptionPlansData, transcriptOrganizationCredentialsData) { // eslint-disable-line max-len + // First destroy old referance to the view if present. + destroyCourseVideoSettingsView(); + courseVideoSettingsView = new CourseVideoSettingsView({ activeTranscriptPreferences: activeTranscriptPreferencesData || null, videoTranscriptSettings: { transcript_preferences_handler_url: transcriptPreferencesUrl, + transcript_credentials_handler_url: transcriptCredentialsHandlerUrl, transcription_plans: transcriptionPlansData || null - } + }, + transcriptOrganizationCredentials: transcriptOrganizationCredentialsData || null }); $courseVideoSettingsEl = courseVideoSettingsView.render().$el; }; @@ -108,10 +143,99 @@ define( $preference.change(); }; - chooseProvider = function(selectedProvider) { + verifyMessage = function(state, message) { + var icon = state === 'error' ? 'fa-info-circle' : 'fa-check-circle'; + expect($courseVideoSettingsEl.find('.course-video-settings-message-wrapper.' + state).html()).toEqual( + '
' + + '' + + '' + message + '' + + '
' + ); + }; + + verifyProviderList = function(selectedProvider) { + var $transcriptProvidersListEl = $courseVideoSettingsEl.find('.transcript-provider-wrapper .transcript-provider-group'); // eslint-disable-line max-len + // Check N/A provider is selected. + expect($transcriptProvidersListEl.find('input[type=radio]:checked').val()).toEqual(selectedProvider.value); // eslint-disable-line max-len + _.each(providers, function(provider, key) { + $transcriptProvidersListEl.find('label[for=transcript-provider-' + key + ']').val(provider.displayName); // eslint-disable-line max-len + }); + }; + + verifyTranscriptPreferences = function() { + 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.selectedTurnaroundPlan, activeTranscriptPreferences.cielo24_turnaround); + expect(courseVideoSettingsView.selectedFidelityPlan, activeTranscriptPreferences.cielo24_fidelity); + expect(courseVideoSettingsView.selectedLanguages, activeTranscriptPreferences.preferred_languages); + }; + + verifyProviderSelectedView = function() { + // Verify provider + expect( + $courseVideoSettingsEl.find('.selected-transcript-provider .title').html() + ).toEqual(courseVideoSettingsView.selectedProvider); + + expect($courseVideoSettingsEl.find('.selected-transcript-provider .action-change-provider')).toExist(); + expect( + $courseVideoSettingsEl.find('.selected-transcript-provider .action-change-provider .sr').html() + ).toEqual('Press change to change selected transcript provider.'); + }; + + verifyTranscriptPreferencesView = function() { + expect($courseVideoSettingsEl.find('.course-video-transcript-preferances-wrapper')).toExist(); + }; + + verifyOrganizationCredentialsView = function() { + expect($courseVideoSettingsEl.find('.organization-credentials-content')).toExist(); + }; + + verifyOrganizationCredentialField = function(fieldName, label) { + var elementSelector = courseVideoSettingsView.selectedProvider + '-' + fieldName; + // Verify that correct label is shown. + expect( + $courseVideoSettingsEl.find('.' + elementSelector + '-wrapper label .title').html() + ).toEqual(label); + + // Verify that credential field is shown. + expect( + $courseVideoSettingsEl.find('.' + elementSelector + '-wrapper .' + elementSelector) + ).toExist(); + }; + + changeProvider = function(selectedProvider) { + // If Provider Selected view is show, first click on "Change Provider" button to + // show all list of providers. + if ($courseVideoSettingsEl.find('.selected-transcript-provider').length) { + $courseVideoSettingsEl.find('.selected-transcript-provider .action-change-provider').click(); + } $courseVideoSettingsEl.find('#transcript-provider-' + selectedProvider).click(); }; + resetProvider = function() { + var requests = AjaxHelpers.requests(this); + // Set no provider selected + changeProvider('none'); + $courseVideoSettingsEl.find('.action-update-course-video-settings').click(); + + AjaxHelpers.expectRequest( + requests, + 'DELETE', + transcriptPreferencesUrl + ); + + // Send successful empty content response. + AjaxHelpers.respondWithJson(requests, {}); + }; + beforeEach(function() { setFixtures( '
' + @@ -148,17 +272,10 @@ define( }); 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('.video-source-language').html()).toEqual(''); - expect($courseVideoSettingsEl.find('.transcript-language-menu').html()).toEqual(''); + renderCourseVideoSettingsView(); + // Checking turnaround is sufficient to check preferences are are shown or not. + expect($courseVideoSettingsEl.find('.transcript-turnaround-wrapper')).not.toExist(); }); it('populates transcription plans correctly', function() { @@ -169,39 +286,17 @@ define( 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('.video-source-language').val()).toEqual( - activeTranscriptPreferences.video_source_language - ); - 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.selectedSourceLanguage, - activeTranscriptPreferences.video_source_language - ); - expect(courseVideoSettingsView.selectedLanguages, activeTranscriptPreferences.preferred_languages); + verifyTranscriptPreferences(); }); it('shows video source language directly in case of 3Play provider', function() { var sourceLanguages, selectedProvider = '3PlayMedia'; - // Select CIELIO24 provider - chooseProvider(selectedProvider); + renderCourseVideoSettingsView(null, transcriptionPlans, transcriptOrganizationCredentials); + + // Select provider + changeProvider(selectedProvider); expect(courseVideoSettingsView.selectedProvider).toEqual(selectedProvider); // Verify source langauges menu is shown. @@ -219,10 +314,10 @@ define( selectedProvider = 'Cielo24', selectedFidelity = 'PROFESSIONAL'; - renderCourseVideoSettingsView(null, transcriptionPlans); + renderCourseVideoSettingsView(null, transcriptionPlans, transcriptOrganizationCredentials); - // Select CIELIO24 provider - chooseProvider(selectedProvider); + // Select provider + changeProvider(selectedProvider); expect(courseVideoSettingsView.selectedProvider).toEqual(selectedProvider); // Verify source language is not shown. @@ -254,8 +349,10 @@ define( selectedProvider = 'Cielo24', selectedFidelity = 'PROFESSIONAL'; - // Select CIELIO24 provider - chooseProvider(selectedProvider); + renderCourseVideoSettingsView(null, transcriptionPlans, transcriptOrganizationCredentials); + + // Select provider + changeProvider(selectedProvider); expect(courseVideoSettingsView.selectedProvider).toEqual(selectedProvider); // Select fidelity @@ -283,8 +380,10 @@ define( selectedProvider = 'Cielo24', selectedFidelity = 'MECHANICAL'; - // Select CIELIO24 provider - chooseProvider(selectedProvider); + renderCourseVideoSettingsView(null, transcriptionPlans, transcriptOrganizationCredentials); + + // Select provider + changeProvider(selectedProvider); expect(courseVideoSettingsView.selectedProvider).toEqual(selectedProvider); // Select fidelity @@ -332,37 +431,16 @@ define( }); // Verify that success message is shown. - expect($courseVideoSettingsEl.find('.course-video-settings-message-wrapper.success').html()).toEqual( - '
' + - '' + - 'Settings updated' + - '
' - ); + verifyMessage('success', 'Settings updated'); }); it('removes transcript settings on update settings button click when no provider is selected', function() { - var requests = AjaxHelpers.requests(this); - - // Set no provider selected - courseVideoSettingsView.selectedProvider = null; - $courseVideoSettingsEl.find('.action-update-course-video-settings').click(); - - AjaxHelpers.expectRequest( - requests, - 'DELETE', - transcriptPreferencesUrl - ); - - // Send successful empty content response. - AjaxHelpers.respondWithJson(requests, {}); + // Reset to N/A provider + resetProvider(); + verifyProviderList(providers.none); // Verify that success message is shown. - expect($courseVideoSettingsEl.find('.course-video-settings-message-wrapper.success').html()).toEqual( - '
' + - '' + - 'Settings updated' + - '
' - ); + verifyMessage('success', 'Settings updated'); }); it('shows error message if server sends error', function() { @@ -390,12 +468,7 @@ define( }); // Verify that error message is shown. - expect($courseVideoSettingsEl.find('.course-video-settings-message-wrapper.error').html()).toEqual( - '
' + - '' + - 'Error message' + - '
' - ); + verifyMessage('error', 'Error message'); }); it('implies preferences are required if not selected when saving preferances', function() { @@ -423,8 +496,274 @@ define( verifyPreferanceErrorState($courseVideoSettingsEl.find('.transcript-languages-wrapper'), false); }); + it('shows provider selected view if active provider is present', function() { + var $selectedProviderContainerEl = $courseVideoSettingsEl.find('.transcript-provider-wrapper .selected-transcript-provider'); // eslint-disable-line max-len + expect($selectedProviderContainerEl.find('span').html()).toEqual(courseVideoSettingsView.selectedProvider); // eslint-disable-line max-len + expect($selectedProviderContainerEl.find('button.action-change-provider')).toExist(); + // Verify provider list view is not shown. + expect($courseVideoSettingsEl.find('.transcript-provider-wrapper .transcript-provider-group')).not.toExist(); // eslint-disable-line max-len + }); + + it('does not show transcript preferences or organization credentials if N/A provider is saved', function() { + renderCourseVideoSettingsView(null, transcriptionPlans); + + // Check N/A provider + resetProvider(); + verifyProviderList(providers.none); + + // Verify selected provider view is not shown. + expect($courseVideoSettingsEl.find('.transcript-provider-wrapper .selected-transcript-provider')).not.toExist(); // eslint-disable-line max-len + }); + + it('does not show transcript preferences or organization credentials if N/A provider is checked', function() { // eslint-disable-line max-len + renderCourseVideoSettingsView(null, transcriptionPlans); + + // Check N/A provider + resetProvider(); + verifyProviderList(providers.none); + + // Verify selected provider view is not shown. + expect($courseVideoSettingsEl.find('.transcript-provider-wrapper .selected-transcript-provider')).not.toExist(); // eslint-disable-line max-len + // Verify transcript preferences are not shown. + expect($courseVideoSettingsEl.find('.course-video-transcript-preferances-wrapper')).not.toExist(); + // Verify org credentials are not shown. + expect($courseVideoSettingsEl.find('.organization-credentials-content')).not.toExist(); + }); + + it('shows organization credentials when organization credentials for selected provider are not present', function() { // eslint-disable-line max-len + renderCourseVideoSettingsView(null, transcriptionPlans); + + // Check Cielo24 provider + changeProvider(providers.Cielo24.key); + verifyProviderList(providers.Cielo24); + + // Verify organization credentials are shown. + verifyOrganizationCredentialsView(); + + // Verify transcript preferences are not shown. + expect($courseVideoSettingsEl.find('.course-video-transcript-preferances-wrapper')).not.toExist(); + }); + + it('shows transcript preferences when organization credentials for selected provider are present', function() { // eslint-disable-line max-len + renderCourseVideoSettingsView(null, transcriptionPlans, transcriptOrganizationCredentials); + + // Check Cielo24 provider + changeProvider('Cielo24'); + verifyProviderList(providers.Cielo24); + + // Verify organization credentials are not shown. + expect($courseVideoSettingsEl.find('.organization-credentials-content')).not.toExist(); + + // Verify transcript preferences are shown. + verifyTranscriptPreferencesView(); + }); + + it('shows organization credentials view if clicked on change provider button', function() { + // Verify organization credentials view is not shown initially. + expect($courseVideoSettingsEl.find('.organization-credentials-content')).not.toExist(); + + verifyProviderSelectedView(); + // Click change button to render organization credentials view. + $courseVideoSettingsEl.find('.action-change-provider').click(); + + // Verify organization credentials is now shown. + verifyOrganizationCredentialsView(); + }); + + it('shows api secret input field if selected provider is 3Play Media', function() { + // Set selected provider to 3Play Media + changeProvider('3PlayMedia'); + + // Click change button to render organization credentials view. + $courseVideoSettingsEl.find('.action-change-provider').click(); + + // Verify 3play api secret and api key are present. + verifyOrganizationCredentialField('api-secret', 'API Secret'); + verifyOrganizationCredentialField('api-key', 'API Key'); + }); + + it('does not show api secret input field if selected provider is not 3Play Media', function() { + verifyProviderSelectedView(); + // Click change button to render organization credentials view. + $courseVideoSettingsEl.find('.action-change-provider').click(); + + // Verify 3Play Media api secret is not present. + expect( + $courseVideoSettingsEl.find('.' + courseVideoSettingsView.selectedProvider + '-api-secret') + ).not.toExist(); + + // Verify api key is present. + verifyOrganizationCredentialField('api-key', 'API Key'); + }); + + it('shows warning message when changing organization credentials if present already', function() { + // Set selectedProvider organization credentials. + courseVideoSettingsView.transcriptOrganizationCredentials[courseVideoSettingsView.selectedProvider] = true; // eslint-disable-line max-len + + verifyProviderSelectedView(); + // Click change button to render organization credentials view. + $courseVideoSettingsEl.find('.action-change-provider').click(); + + // Verify credentials are shown + verifyOrganizationCredentialsView(); + // Verify warning message is shown. + expect($courseVideoSettingsEl.find('.transcription-account-details.warning')).toExist(); + // Verify message + expect($courseVideoSettingsEl.find('.transcription-account-details').html()).toEqual( + 'By entering the set of credntials below, ' + + 'you will be overwriting your organization\'s credentials.' + ); + }); + + it('does not show warning message when changing organization credentials if not present already', function() { // eslint-disable-line max-len + verifyProviderSelectedView(); + // Click change button to render organization credentials view. + $courseVideoSettingsEl.find('.action-change-provider').click(); + + // Verify warning message is not shown. + expect($courseVideoSettingsEl.find('.transcription-account-details.warning')).not.toExist(); + // Initial detail message is shown instead. + expect($courseVideoSettingsEl.find('.transcription-account-details').html()).toEqual( + 'Please enter your organization\'s account information. ' + + 'Remember that all courses in your organization will use this account.' + ); + }); + + it('shows validation errors if no organization credentials are provided when saving credentials', function() { // eslint-disable-line max-len + // Set selected provider to 3Play Media + changeProvider('3PlayMedia'); + + // Click save organization credentials button to save credentials. + $courseVideoSettingsEl.find('.action-update-org-credentials').click(); + + verifyPreferanceErrorState( + $courseVideoSettingsEl.find('.' + courseVideoSettingsView.selectedProvider + '-api-key-wrapper'), + true + ); + + verifyPreferanceErrorState( + $courseVideoSettingsEl.find('.' + courseVideoSettingsView.selectedProvider + '-api-secret-wrapper'), + true + ); + }); + + it('saves organization credentials on clicking save credentials button', function() { + var requests = AjaxHelpers.requests(this); + + verifyProviderSelectedView(); + // Click change button to render organization credentials view. + $courseVideoSettingsEl.find('.action-change-provider').click(); + + // Set organization credentials so as to pass validations. + $courseVideoSettingsEl.find('.' + courseVideoSettingsView.selectedProvider + '-api-key').val('testkey'); + + // Click save organization credentials button to save credentials. + $courseVideoSettingsEl.find('.action-update-org-credentials').click(); + + AjaxHelpers.expectRequest( + requests, + 'POST', + transcriptCredentialsHandlerUrl, + JSON.stringify({ + provider: activeTranscriptPreferences.provider, + global: false + }) + ); + + // Send empty response. + AjaxHelpers.respondWithJson(requests, {}); + + // Verify that success message is shown. + verifyMessage( + 'success', + transcriptionPlans[courseVideoSettingsView.selectedProvider].display_name + ' credentials saved' + ); + }); + + it('shows selected provider view afer organization credentials saved', function() { + var requests = AjaxHelpers.requests(this); + renderCourseVideoSettingsView(null, transcriptionPlans); + + // Verify selected provider view is not shown. + expect( + $courseVideoSettingsEl.find('.transcript-provider-wrapper .selected-transcript-provider') + ).not.toExist(); + + // Verify provider list view is shown. + verifyProviderList(providers.none); + + // Check Cielo24 provider + changeProvider('Cielo24'); + verifyProviderList(providers.Cielo24); + + // Set organization credentials so as to pass validations. + $courseVideoSettingsEl.find('.' + courseVideoSettingsView.selectedProvider + '-api-key').val('testkey'); + + // Click save organization credentials button to save credentials. + $courseVideoSettingsEl.find('.action-update-org-credentials').click(); + + AjaxHelpers.expectRequest( + requests, + 'POST', + transcriptCredentialsHandlerUrl, + JSON.stringify({ + provider: activeTranscriptPreferences.provider, + global: false + }) + ); + + // Send empty response. + AjaxHelpers.respondWithJson(requests, {}); + + // Verify that success message is shown. + verifyMessage( + 'success', + transcriptionPlans[courseVideoSettingsView.selectedProvider].display_name + ' credentials saved' + ); + + // Shows selected provider view after credentials are saved. + verifyProviderSelectedView(); + + // Verify provider list view is not shown. + expect( + $courseVideoSettingsEl.find('.transcript-provider-wrapper .transcript-provider-group') + ).not.toExist(); + }); + + it('shows error message on saving organization credentials if server sends error', function() { + var requests = AjaxHelpers.requests(this); + + verifyProviderSelectedView(); + // Click change button to render organization credentials view. + $courseVideoSettingsEl.find('.action-change-provider').click(); + + // Set organization credentials so as to pass validations. + $courseVideoSettingsEl.find('.' + courseVideoSettingsView.selectedProvider + '-api-key').val('testkey'); + + // Click save organization credentials button to save credentials. + $courseVideoSettingsEl.find('.action-update-org-credentials').click(); + + AjaxHelpers.expectRequest( + requests, + 'POST', + transcriptCredentialsHandlerUrl, + JSON.stringify({ + provider: activeTranscriptPreferences.provider, + global: false + }) + ); + + // Send error response. + AjaxHelpers.respondWithError(requests, 400, { + error: 'Error message' + }); + + // Verify that error message is shown. + verifyMessage('error', 'Error message'); + }); + // 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. + // for specific preferance selected tests etc. - See EDUCATOR-1478 }); } ); diff --git a/cms/static/js/views/active_video_upload_list.js b/cms/static/js/views/active_video_upload_list.js index 88afe648dc..6b0d067927 100644 --- a/cms/static/js/views/active_video_upload_list.js +++ b/cms/static/js/views/active_video_upload_list.js @@ -42,6 +42,7 @@ define([ this.concurrentUploadLimit = options.concurrentUploadLimit || 0; this.postUrl = options.postUrl; this.activeTranscriptPreferences = options.activeTranscriptPreferences; + this.transcriptOrganizationCredentials = options.transcriptOrganizationCredentials; this.videoTranscriptSettings = options.videoTranscriptSettings; this.isVideoTranscriptEnabled = options.isVideoTranscriptEnabled; this.videoSupportedFileFormats = options.videoSupportedFileFormats; @@ -85,6 +86,7 @@ define([ if (this.isVideoTranscriptEnabled) { this.courseVideoSettingsView = new CourseVideoSettingsView({ activeTranscriptPreferences: this.activeTranscriptPreferences, + transcriptOrganizationCredentials: this.transcriptOrganizationCredentials, videoTranscriptSettings: this.videoTranscriptSettings }); this.courseVideoSettingsView.render(); diff --git a/cms/static/js/views/course_video_settings.js b/cms/static/js/views/course_video_settings.js index bbaa675af5..b7ae613dfc 100644 --- a/cms/static/js/views/course_video_settings.js +++ b/cms/static/js/views/course_video_settings.js @@ -3,11 +3,20 @@ */ define([ 'jquery', 'backbone', 'underscore', 'gettext', 'moment', + 'common/js/components/utils/view_utils', 'edx-ui-toolkit/js/utils/html-utils', 'edx-ui-toolkit/js/utils/string-utils', - 'text!templates/course-video-settings.underscore' + 'text!templates/course-video-settings.underscore', + 'text!templates/course-video-transcript-preferences.underscore', + 'text!templates/course-video-transcript-provider-empty.underscore', + 'text!templates/course-video-transcript-provider-selected.underscore', + 'text!templates/transcript-organization-credentials.underscore', + 'text!templates/course-video-settings-update-settings-footer.underscore', + 'text!templates/course-video-settings-update-org-credentials-footer.underscore' ], -function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSettingsTemplate) { +function($, Backbone, _, gettext, moment, ViewUtils, HtmlUtils, StringUtils, TranscriptSettingsTemplate, + TranscriptPreferencesTemplate, TranscriptProviderEmptyStateTemplate, TranscriptProviderSelectedStateTemplate, + OrganizationCredentialsTemplate, UpdateSettingsFooterTemplate, OrganizationCredentialsFooterTemplate) { 'use strict'; var CourseVideoSettingsView, @@ -24,6 +33,8 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett 'change #video-source-language': 'videoSourceLanguageSelected', 'click .action-add-language': 'languageSelected', 'click .action-remove-language': 'languageRemoved', + 'click .action-change-provider': 'renderOrganizationCredentials', + 'click .action-update-org-credentials': 'updateOrganizationCredentials', 'click .action-update-course-video-settings': 'updateCourseVideoSettings', 'click .action-close-course-video-settings': 'closeCourseVideoSettings' }, @@ -31,9 +42,17 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett initialize: function(options) { var videoTranscriptSettings = options.videoTranscriptSettings; this.activeTranscriptionPlan = options.activeTranscriptPreferences; + this.transcriptOrganizationCredentials = _.extend({}, options.transcriptOrganizationCredentials); this.availableTranscriptionPlans = videoTranscriptSettings.transcription_plans; this.transcriptHandlerUrl = videoTranscriptSettings.transcript_preferences_handler_url; + this.transcriptCredentialsHandlerUrl = videoTranscriptSettings.transcript_credentials_handler_url; this.template = HtmlUtils.template(TranscriptSettingsTemplate); + this.transcriptPreferencesTemplate = HtmlUtils.template(TranscriptPreferencesTemplate); + this.organizationCredentialsTemplate = HtmlUtils.template(OrganizationCredentialsTemplate); + this.organizationCredentialsFooterTemplate = HtmlUtils.template(OrganizationCredentialsFooterTemplate); + this.updateSettingsFooterTemplate = HtmlUtils.template(UpdateSettingsFooterTemplate); + this.transcriptProviderEmptyStateTemplate = HtmlUtils.template(TranscriptProviderEmptyStateTemplate); + this.transcriptProviderSelectedStateTemplate = HtmlUtils.template(TranscriptProviderSelectedStateTemplate); this.setActiveTranscriptPlanData(); this.selectedLanguages = []; }, @@ -49,7 +68,7 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett // 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 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(); } @@ -158,9 +177,33 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett }, providerSelected: function(event) { + var $courseVideoSettingsContentEl = this.$el.find('.course-video-settings-content'), + dateModified = this.activeTranscriptionPlan ? + moment.utc(this.activeTranscriptionPlan.modified).format('ll') : ''; + this.resetPlanData(); this.selectedProvider = event.target.value; - this.renderPreferences(); + + if (!this.selectedProvider) { + // Hide organization credentials and transcript preferences views + $courseVideoSettingsContentEl.hide(); + + // Render footer + HtmlUtils.setHtml( + this.$el.find('.course-video-settings-footer'), + this.updateSettingsFooterTemplate({ + dateModified: dateModified + }) + ); + return; + } + $courseVideoSettingsContentEl.show(); + // If org provider specific credentials are present + if (this.transcriptOrganizationCredentials[this.selectedProvider]) { + this.renderTranscriptPreferences(); + } else { + this.renderOrganizationCredentials(); + } }, languageSelected: function(event) { @@ -187,43 +230,56 @@ function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSett $(event.target.parentElement).parent().remove(); // Remove language from selected languages. - this.selectedLanguages = _.without(this.selectedLanguages, selectedLanguage); + this.selectedLanguages = this.activeLanguages = _.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'); + renderProviders: function(state) { + var $transcriptProviderWrapperEl = this.$el.find('.transcript-provider-wrapper'); + if (!state) { + state = this.selectedProvider ? 'selected' : 'empty'; // eslint-disable-line no-param-reassign + } - if (providerPlan) { + // If no transcription plans are sentm return. + if (!this.availableTranscriptionPlans) { + return; + } + if (state === 'empty') { HtmlUtils.setHtml( - $providerEl, - HtmlUtils.interpolateHtml( - HtmlUtils.HTML(''), // 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('