Commit 42f44848 by Andy Armstrong

Merge pull request #12585 from edx/andya/course-navigation-safe-template

Refactor staff preview menu logic out of template
parents 53d31551 7ee41eaf
**/vendor **/vendor
node_modules
cms/static/js/i18n/**/*.js cms/static/js/i18n/**/*.js
lms/static/js/i18n/**/*.js lms/static/js/i18n/**/*.js
lms/static/lms/js/build.js
lms/static/lms/js/spec/main.js
node_modules
venv venv
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
"immed" : true, // Prohibits the use of immediate function invocations without wrapping them in parentheses. "immed" : true, // Prohibits the use of immediate function invocations without wrapping them in parentheses.
// "indent" : 4, // Enforces specific tab width for your code. Has no effect when "white" option is not used. // "indent" : 4, // Enforces specific tab width for your code. Has no effect when "white" option is not used.
"latedef" : "nofunc", // Prohibits the use of a variable before it was defined. Setting this option to "nofunc" will allow function declarations to be ignored. "latedef" : "nofunc", // Prohibits the use of a variable before it was defined. Setting this option to "nofunc" will allow function declarations to be ignored.
"newcap" : true, // Requires you to capitalize names of constructor functions. "newcap" : false, // Requires you to capitalize names of constructor functions.
"noarg" : true, // Prohibits the use of arguments.caller and arguments.callee. "noarg" : true, // Prohibits the use of arguments.caller and arguments.callee.
"noempty" : true, // Warns when you have an empty block in your code. "noempty" : true, // Warns when you have an empty block in your code.
"nonbsp" : true, // Warns about "non-breaking whitespace" characters. "nonbsp" : true, // Warns about "non-breaking whitespace" characters.
......
...@@ -29,12 +29,14 @@ var options = { ...@@ -29,12 +29,14 @@ var options = {
sourceFiles: [ sourceFiles: [
{pattern: 'coffee/src/**/!(*spec).js'}, {pattern: 'coffee/src/**/!(*spec).js'},
{pattern: 'js/**/!(*spec|djangojs).js'}, {pattern: 'js/**/!(*spec|djangojs).js'},
{pattern: 'lms/js/**/!(*spec).js'},
{pattern: 'support/js/**/!(*spec).js'}, {pattern: 'support/js/**/!(*spec).js'},
{pattern: 'teams/js/**/!(*spec).js'} {pattern: 'teams/js/**/!(*spec).js'}
], ],
specFiles: [ specFiles: [
{pattern: 'js/spec/**/*spec.js'}, {pattern: 'js/spec/**/*spec.js'},
{pattern: 'lms/js/spec/**/*spec.js'},
{pattern: 'support/js/spec/**/*spec.js'}, {pattern: 'support/js/spec/**/*spec.js'},
{pattern: 'teams/js/spec/**/*spec.js'}, {pattern: 'teams/js/spec/**/*spec.js'},
{pattern: 'xmodule_js/common_static/coffee/spec/**/*.js'} {pattern: 'xmodule_js/common_static/coffee/spec/**/*.js'}
...@@ -42,13 +44,14 @@ var options = { ...@@ -42,13 +44,14 @@ var options = {
fixtureFiles: [ fixtureFiles: [
{pattern: 'js/fixtures/**/*.html'}, {pattern: 'js/fixtures/**/*.html'},
{pattern: 'lms/fixtures/**/*.html'},
{pattern: 'support/templates/**/*.*'}, {pattern: 'support/templates/**/*.*'},
{pattern: 'teams/templates/**/*.*'}, {pattern: 'teams/templates/**/*.*'},
{pattern: 'templates/**/*.*'} {pattern: 'templates/**/*.*'}
], ],
runFiles: [ runFiles: [
{pattern: 'js/spec/main.js', included: true} {pattern: 'lms/js/spec/main.js', included: true}
] ]
}; };
......
...@@ -16,7 +16,7 @@ var options = { ...@@ -16,7 +16,7 @@ var options = {
// Avoid adding files to this list. Use RequireJS. // Avoid adding files to this list. Use RequireJS.
libraryFilesToInclude: [ libraryFilesToInclude: [
{pattern: 'xmodule_js/common_static/js/vendor/requirejs/require.js', included: true}, {pattern: 'xmodule_js/common_static/js/vendor/requirejs/require.js', included: true},
{pattern: 'js/spec/main_requirejs_coffee.js', included: true}, {pattern: 'lms/js/spec/main_requirejs_coffee.js', included: true},
{pattern: 'js/RequireJS-namespace-undefine.js', included: true}, {pattern: 'js/RequireJS-namespace-undefine.js', included: true},
{pattern: 'xmodule_js/common_static/coffee/src/ajax_prefix.js', included: true}, {pattern: 'xmodule_js/common_static/coffee/src/ajax_prefix.js', included: true},
......
<nav class="wrapper-preview-menu" aria-label="Course View">
<div class="preview-menu">
<ol class="preview-actions">
<li class="action-preview">
<form action="#" class="action-preview-form" method="post">
<label for="action-preview-select" class="action-preview-label">View this course as:</label>
<select class="action-preview-select" id="action-preview-select" name="select">
<option value="staff" selected>Staff</option>
<option value="student">Student</option>
<option value="specific student">Specific student</option>
<option value="group-a" data-group-id="group-a">Student in Group A</option>
<option value="group-b" data-group-id="group-b">Student in Group B</option>
</select>
<div class="action-preview-username-container">
<label for="action-preview-username" class="action-preview-label">Username or email:</label>
<input type="text" class="action-preview-username" id="action-preview-username">
</div>
<button type="submit" class="sr" name="submit" value="submit">Set preview mode</button>
</form>
</li>
</ol>
</div>
</nav>
(function () { (function() {
'use strict'; 'use strict';
var getModulesList = function (modules) { var getModulesList = function(modules) {
return modules.map(function (moduleName) { return modules.map(function(moduleName) {
return { name: moduleName }; return {name: moduleName};
}); });
}; };
...@@ -11,19 +11,23 @@ ...@@ -11,19 +11,23 @@
process.env.REQUIRE_BUILD_PROFILE_OPTIMIZE : 'uglify2'; process.env.REQUIRE_BUILD_PROFILE_OPTIMIZE : 'uglify2';
return { return {
namespace: "RequireJS", namespace: 'RequireJS',
/** /**
* List the modules that will be optimized. All their immediate and deep * List the modules that will be optimized. All their immediate and deep
* dependencies will be included in the module's file when the build is * dependencies will be included in the module's file when the build is
* done. * done.
*/ */
modules: getModulesList([ modules: getModulesList([
'js/api_admin/catalog_preview_factory',
'js/courseware/courseware_factory',
'js/discovery/discovery_factory', 'js/discovery/discovery_factory',
'js/edxnotes/views/notes_visibility_factory', 'js/edxnotes/views/notes_visibility_factory',
'js/edxnotes/views/page_factory', 'js/edxnotes/views/page_factory',
'js/financial-assistance/financial_assistance_form_factory', 'js/financial-assistance/financial_assistance_form_factory',
'js/groups/views/cohorts_dashboard_factory', 'js/groups/views/cohorts_dashboard_factory',
'js/header_factory', 'js/header_factory',
'js/learner_dashboard/program_details_factory',
'js/learner_dashboard/program_list_factory',
'js/search/course/course_search_factory', 'js/search/course/course_search_factory',
'js/search/dashboard/dashboard_search_factory', 'js/search/dashboard/dashboard_search_factory',
'js/student_account/logistration_factory', 'js/student_account/logistration_factory',
...@@ -31,13 +35,10 @@ ...@@ -31,13 +35,10 @@
'js/student_account/views/finish_auth_factory', 'js/student_account/views/finish_auth_factory',
'js/student_profile/views/learner_profile_factory', 'js/student_profile/views/learner_profile_factory',
'js/views/message_banner', 'js/views/message_banner',
'teams/js/teams_tab_factory', 'lms/js/preview/preview_factory',
'support/js/certificates_factory', 'support/js/certificates_factory',
'support/js/enrollment_factory', 'support/js/enrollment_factory',
'js/courseware/courseware_factory', 'teams/js/teams_tab_factory'
'js/learner_dashboard/program_details_factory',
'js/learner_dashboard/program_list_factory',
'js/api_admin/catalog_preview_factory'
]), ]),
/** /**
...@@ -91,7 +92,7 @@ ...@@ -91,7 +92,7 @@
/** /**
* Stub out requireJS text in the optimized file, but leave available for non-optimized development use. * Stub out requireJS text in the optimized file, but leave available for non-optimized development use.
*/ */
stubModules: ["text"], stubModules: ['text'],
/** /**
* If shim config is used in the app during runtime, duplicate the config * If shim config is used in the app during runtime, duplicate the config
...@@ -161,4 +162,4 @@ ...@@ -161,4 +162,4 @@
*/ */
logLevel: 1 logLevel: 1
}; };
} ()) }())
;(function(define) {
'use strict';
define(['jquery', 'common/js/components/utils/view_utils'],
function($, ViewUtils) {
return function(options) {
var $selectElement = $('.action-preview-select'),
$userNameElement = $('.action-preview-username'),
$userNameContainer = $('.action-preview-username-container');
if (options.disableStudentAccess) {
$selectElement.attr('disabled', true);
$selectElement.attr('title', gettext('Course is not yet visible to students.'));
}
if (options.specificStudentSelected) {
$userNameContainer.css('display', 'inline-block');
$userNameElement.val(options.masqueradeUsername);
}
$selectElement.change(function() {
var selectedOption;
if ($selectElement.attr('disabled')) {
return alert(gettext('You cannot view the course as a student or beta tester before the course release date.')); // jshint ignore:line
}
selectedOption = $selectElement.find('option:selected');
if (selectedOption.val() === 'specific student') {
$userNameContainer.css('display', 'inline-block');
} else {
$userNameContainer.hide();
masquerade(selectedOption);
}
});
$userNameElement.keypress(function(event) {
if (event.keyCode === 13) {
// Avoid submitting the form on enter, since the submit action isn't implemented.
// Instead, blur the element to trigger a change event in case the value was edited,
// which in turn will trigger an AJAX request to update the masquerading data.
$userNameElement.blur();
return false;
}
return true;
});
$userNameElement.change(function() {
masquerade($selectElement.find('option:selected'));
});
function masquerade(selectedOption) {
var data = {
role: selectedOption.val() === 'staff' ? 'staff' : 'student',
user_partition_id: options.cohortedUserPartitionId,
group_id: selectedOption.data('group-id'),
user_name: selectedOption.val() === 'specific student' ? $userNameElement.val() : null
};
$.ajax({
url: '/courses/' + options.courseId + '/masquerade',
type: 'POST',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(data),
success: function(result) {
if (result.success) {
ViewUtils.reload();
} else {
alert(result.error);
}
},
error: function() {
alert('Error: cannot connect to server');
}
});
}
};
});
}).call(this, define || RequireJS.define);
(function(requirejs, define) { (function(requirejs) {
'use strict'; 'use strict';
// TODO: how can we share the vast majority of this config that is in common with CMS? // TODO: how can we share the vast majority of this config that is in common with CMS?
...@@ -120,7 +120,7 @@ ...@@ -120,7 +120,7 @@
'date': { 'date': {
exports: 'Date' exports: 'Date'
}, },
"jquery-migrate": ['jquery'], 'jquery-migrate': ['jquery'],
'jquery.ui': { 'jquery.ui': {
deps: ['jquery'], deps: ['jquery'],
exports: 'jQuery.ui' exports: 'jQuery.ui'
...@@ -204,8 +204,8 @@ ...@@ -204,8 +204,8 @@
deps: ['backbone'], deps: ['backbone'],
exports: 'Backbone.PageableCollection' exports: 'Backbone.PageableCollection'
}, },
"backbone-super": { 'backbone-super': {
deps: ["backbone"] deps: ['backbone']
}, },
'paging-collection': { 'paging-collection': {
deps: ['jquery', 'underscore', 'backbone.paginator'] deps: ['jquery', 'underscore', 'backbone.paginator']
...@@ -292,13 +292,13 @@ ...@@ -292,13 +292,13 @@
}, },
'coffee/src/instructor_dashboard/util': { 'coffee/src/instructor_dashboard/util': {
exports: 'coffee/src/instructor_dashboard/util', exports: 'coffee/src/instructor_dashboard/util',
deps: ['jquery', 'gettext'], deps: ['jquery', 'underscore', 'slick.core', 'slick.grid'],
init: function() { init: function() {
// Set global variables that the util code is expecting to be defined // Set global variables that the util code is expecting to be defined
require([ require([
'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'
], function (HtmlUtils, StringUtils) { ], function(HtmlUtils, StringUtils) {
window.edx = edx || {}; window.edx = edx || {};
window.edx.HtmlUtils = HtmlUtils; window.edx.HtmlUtils = HtmlUtils;
window.edx.StringUtils = StringUtils; window.edx.StringUtils = StringUtils;
...@@ -309,10 +309,6 @@ ...@@ -309,10 +309,6 @@
exports: 'coffee/src/instructor_dashboard/student_admin', exports: 'coffee/src/instructor_dashboard/student_admin',
deps: ['jquery', 'underscore', 'coffee/src/instructor_dashboard/util', 'string_utils'] deps: ['jquery', 'underscore', 'coffee/src/instructor_dashboard/util', 'string_utils']
}, },
'coffee/src/instructor_dashboard/util': {
exports: 'coffee/src/instructor_dashboard/util',
deps: ['jquery', 'underscore', 'slick.core', 'slick.grid']
},
'js/instructor_dashboard/certificates': { 'js/instructor_dashboard/certificates': {
exports: 'js/instructor_dashboard/certificates', exports: 'js/instructor_dashboard/certificates',
deps: ['jquery', 'gettext', 'underscore'] deps: ['jquery', 'gettext', 'underscore']
...@@ -369,11 +365,11 @@ ...@@ -369,11 +365,11 @@
}, },
'js/verify_student/models/verification_model': { 'js/verify_student/models/verification_model': {
exports: 'edx.verify_student.VerificationModel', exports: 'edx.verify_student.VerificationModel',
deps: [ 'jquery', 'underscore', 'backbone', 'jquery.cookie' ] deps: ['jquery', 'underscore', 'backbone', 'jquery.cookie']
}, },
'js/verify_student/views/error_view': { 'js/verify_student/views/error_view': {
exports: 'edx.verify_student.ErrorView', exports: 'edx.verify_student.ErrorView',
deps: [ 'jquery', 'underscore', 'backbone' ] deps: ['jquery', 'underscore', 'backbone']
}, },
'js/verify_student/views/webcam_photo_view': { 'js/verify_student/views/webcam_photo_view': {
exports: 'edx.verify_student.WebcamPhotoView', exports: 'edx.verify_student.WebcamPhotoView',
...@@ -387,11 +383,11 @@ ...@@ -387,11 +383,11 @@
}, },
'js/verify_student/views/image_input_view': { 'js/verify_student/views/image_input_view': {
exports: 'edx.verify_student.ImageInputView', exports: 'edx.verify_student.ImageInputView',
deps: [ 'jquery', 'underscore', 'backbone', 'gettext' ] deps: ['jquery', 'underscore', 'backbone', 'gettext']
}, },
'js/verify_student/views/step_view': { 'js/verify_student/views/step_view': {
exports: 'edx.verify_student.StepView', exports: 'edx.verify_student.StepView',
deps: [ 'jquery', 'underscore', 'underscore.string', 'backbone', 'gettext' ], deps: ['jquery', 'underscore', 'underscore.string', 'backbone', 'gettext'],
init: function() { init: function() {
// Set global variables that the payment code is expecting to be defined // Set global variables that the payment code is expecting to be defined
require([ require([
...@@ -538,7 +534,7 @@ ...@@ -538,7 +534,7 @@
exports: 'DiscussionUtil', exports: 'DiscussionUtil',
init: function() { init: function() {
// Set global variables that the discussion code is expecting to be defined // Set global variables that the discussion code is expecting to be defined
require(['backbone', 'URI'], function (Backbone, URI) { require(['backbone', 'URI'], function(Backbone, URI) {
window.Backbone = Backbone; window.Backbone = Backbone;
window.URI = URI; window.URI = URI;
}); });
...@@ -686,6 +682,7 @@ ...@@ -686,6 +682,7 @@
}); });
var testFiles = [ var testFiles = [
'lms/js/spec/preview/preview_factory_spec.js',
'js/spec/api_admin/catalog_preview_spec.js', 'js/spec/api_admin/catalog_preview_spec.js',
'js/spec/courseware/bookmark_button_view_spec.js', 'js/spec/courseware/bookmark_button_view_spec.js',
'js/spec/courseware/bookmarks_list_view_spec.js', 'js/spec/courseware/bookmarks_list_view_spec.js',
...@@ -821,8 +818,8 @@ ...@@ -821,8 +818,8 @@
// Jasmine has a global stack for creating a tree of specs. We need to load // Jasmine has a global stack for creating a tree of specs. We need to load
// spec files one by one, otherwise some end up getting nested under others. // spec files one by one, otherwise some end up getting nested under others.
window.requireSerial(specHelpers.concat(testFiles), function () { window.requireSerial(specHelpers.concat(testFiles), function() {
// start test run, once Require.js is done // start test run, once Require.js is done
window.__karma__.start(); window.__karma__.start();
}); });
}).call(this, requirejs, define); }).call(this, requirejs);
define(
[
'common/js/spec_helpers/ajax_helpers',
'common/js/components/utils/view_utils',
'lms/js/preview/preview_factory'
],
function(AjaxHelpers, ViewUtils, PreviewFactory) {
'use strict';
describe('Preview Factory', function() {
var showPreview,
previewActionSelect,
usernameInput;
showPreview = function(options) {
PreviewFactory(options);
};
previewActionSelect = function() {
return $('.action-preview-select');
};
usernameInput = function() {
return $('.action-preview-username');
};
beforeEach(function() {
loadFixtures('lms/fixtures/preview/course_preview.html');
});
it('can render preview for a staff user', function() {
showPreview({
courseId: 'test_course'
});
expect(previewActionSelect().val()).toBe('staff');
});
it('can disable course access for a student', function() {
var select;
showPreview({
courseId: 'test_course',
disableStudentAccess: true
});
select = previewActionSelect();
expect(select.attr('disabled')).toBe('disabled');
expect(select.attr('title')).toBe('Course is not yet visible to students.');
});
it('can switch to view as a student', function() {
var requests = AjaxHelpers.requests(this),
reloadSpy = spyOn(ViewUtils, 'reload');
showPreview({
courseId: 'test_course'
});
previewActionSelect().find('option[value="student"]').prop('selected', 'selected').change();
AjaxHelpers.expectJsonRequest(
requests, 'POST', '/courses/test_course/masquerade',
{
role: 'student',
user_name: null
}
);
AjaxHelpers.respondWithJson(requests, {
success: true
});
expect(reloadSpy).toHaveBeenCalled();
});
it('can switch to view as a content group', function() {
var requests = AjaxHelpers.requests(this),
reloadSpy = spyOn(ViewUtils, 'reload');
showPreview({
cohortedUserPartitionId: 'test_partition_id',
courseId: 'test_course'
});
previewActionSelect().find('option[value="group-b"]').prop('selected', 'selected').change();
AjaxHelpers.expectJsonRequest(
requests, 'POST', '/courses/test_course/masquerade',
{
role: 'student',
user_name: null,
user_partition_id: 'test_partition_id',
group_id: 'group-b'
}
);
AjaxHelpers.respondWithJson(requests, {
success: true
});
expect(reloadSpy).toHaveBeenCalled();
});
it('can switch to masquerade as a specific student', function() {
var requests = AjaxHelpers.requests(this),
reloadSpy = spyOn(ViewUtils, 'reload');
showPreview({
courseId: 'test_course'
});
previewActionSelect().find('option[value="specific student"]').prop('selected', 'selected').change();
usernameInput().val('test_user').change();
AjaxHelpers.expectJsonRequest(
requests, 'POST', '/courses/test_course/masquerade',
{
role: 'student',
user_name: 'test_user'
}
);
AjaxHelpers.respondWithJson(requests, {
success: true
});
expect(reloadSpy).toHaveBeenCalled();
});
it('shows the correct information when masquerading as a specific student', function() {
showPreview({
specificStudentSelected: true,
masqueradeUsername: 'test_user'
});
expect(usernameInput().val()).toBe('test_user');
});
});
}
);
...@@ -6,9 +6,11 @@ from courseware.tabs import get_course_tab_list ...@@ -6,9 +6,11 @@ from courseware.tabs import get_course_tab_list
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.conf import settings from django.conf import settings
from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_user_partition from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_user_partition
from openedx.core.djangolib.js_utils import dump_js_escaped_json
from openedx.core.djangolib.markup import HTML, Text
from student.models import CourseEnrollment from student.models import CourseEnrollment
%> %>
<%page args="active_page=None" /> <%page args="active_page=None" expression_filter="h" />
<% <%
if active_page is None and active_page_context is not UNDEFINED: if active_page is None and active_page_context is not UNDEFINED:
...@@ -29,13 +31,13 @@ include_special_exams = settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False) and ...@@ -29,13 +31,13 @@ include_special_exams = settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False) and
%> %>
% if include_special_exams: % if include_special_exams:
<%static:js group='proctoring'/> <%static:js group='proctoring'/>
% for template_name in ["proctored-exam-status"]: % for template_name in ["proctored-exam-status"]:
<script type="text/template" id="${template_name}-tpl"> <script type="text/template" id="${template_name}-tpl">
<%static:include path="courseware/${template_name}.underscore" /> <%static:include path="courseware/${template_name}.underscore" />
</script> </script>
% endfor % endfor
<div class="proctored_exam_status"></div> <div class="proctored_exam_status"></div>
% endif % endif
% if show_preview_menu: % if show_preview_menu:
<nav class="wrapper-preview-menu" aria-label="${_('Course View')}"> <nav class="wrapper-preview-menu" aria-label="${_('Course View')}">
...@@ -65,102 +67,46 @@ include_special_exams = settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False) and ...@@ -65,102 +67,46 @@ include_special_exams = settings.FEATURES.get('ENABLE_SPECIAL_EXAMS', False) and
</li> </li>
</ol> </ol>
% if specific_student_selected: % if specific_student_selected:
<div class="preview-specific-student-notice"> <div class="preview-specific-student-notice">
<p> <p>
${_("You are now viewing the course as <i>{user_name}</i>.").format(user_name=masquerade_user_name)} ${Text(_("You are now viewing the course as {i_start}{user_name}{i_end}.")).format(
</p> user_name=masquerade_user_name,
</div> i_start=HTML(u'<i>'),
i_end=HTML(u'</i>'),
)}
</p>
</div>
% endif % endif
</div> </div>
</nav> </nav>
% endif % endif
% if disable_tabs is UNDEFINED or not disable_tabs: % if disable_tabs is UNDEFINED or not disable_tabs:
<nav class="${active_page} wrapper-course-material" aria-label="${_('Course Material')}"> <nav class="${active_page} wrapper-course-material" aria-label="${_('Course Material')}">
<div class="course-material"> <div class="course-material">
<% <%
tab_list = get_course_tab_list(request, course) tab_list = get_course_tab_list(request, course)
tabs_tmpl = static.get_template_path('/courseware/tabs.html') tabs_tmpl = static.get_template_path('/courseware/tabs.html')
%> %>
<ol class="course-tabs"> <ol class="course-tabs">
<%include file="${tabs_tmpl}" args="tab_list=tab_list,active_page=active_page,default_tab=default_tab,tab_image=tab_image" /> <%include file="${tabs_tmpl}" args="tab_list=tab_list,active_page=active_page,default_tab=default_tab,tab_image=tab_image" />
<%block name="extratabs" /> <%block name="extratabs" />
</ol> </ol>
</div> </div>
</nav> </nav>
%endif %endif
% if show_preview_menu: % if show_preview_menu:
<script type="text/javascript"> <%
(function() { preview_options = {
var selectElement = $('.action-preview-select'); "courseId": course.id,
var userNameElement = $('#action-preview-username'); "disableStudentAccess": disable_student_access if disable_student_access is not UNDEFINED else False,
var userNameContainer = $('.action-preview-username-container') "specificStudentSelected": specific_student_selected,
"cohortedUserPartitionId": cohorted_user_partition.id if cohorted_user_partition else None,
% if disable_student_access: "masqueradeUsername" : masquerade_user_name if masquerade_user_name is not UNDEFINED else None,
selectElement.attr("disabled", true);
selectElement.attr("title", "${_("Course is not yet visible to students.")}");
% endif
% if specific_student_selected:
userNameContainer.css('display', 'inline-block');
userNameElement.val('${masquerade_user_name}');
% endif
selectElement.change(function() {
var selectedOption;
if (selectElement.attr("disabled")) {
return alert("${_("You cannot view the course as a student or beta tester before the course release date.")}");
}
selectedOption = selectElement.find('option:selected');
if (selectedOption.val() === 'specific student') {
userNameContainer.css('display', 'inline-block');
} else {
userNameContainer.hide();
masquerade(selectedOption);
}
});
userNameElement.keypress(function(event) {
if (event.keyCode === 13) {
// Avoid submitting the form on enter, since the submit action isn't implemented. Instead, blur the
// element to trigger a change event in case the value was edited, which in turn will trigger an AJAX
// request to update the masquerading data.
userNameElement.blur();
return false;
}
return true;
});
userNameElement.change(function() {
masquerade(selectElement.find('option:selected'));
});
function masquerade(selectedOption) {
var data = {
role: selectedOption.val() === 'staff' ? 'staff' : 'student',
user_partition_id: ${cohorted_user_partition.id if cohorted_user_partition else 'null'},
group_id: selectedOption.data('group-id'),
user_name: selectedOption.val() === 'specific student' ? userNameElement.val() : null
};
$.ajax({
url: '/courses/${course.id}/masquerade',
type: 'POST',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(data),
success: function(result) {
if (result.success) {
location.reload();
} else {
alert(result.error);
}
},
error: function() {
alert('Error: cannot connect to server');
}
});
} }
}()); %>
</script> <%static:require_module module_name="lms/js/preview/preview_factory" class_name="PreviewFactory">
PreviewFactory(${preview_options | n, dump_js_escaped_json});
</%static:require_module>
% endif % endif
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