This commit adds validation for course advanced settings. Currently when course
administrators make invalid changes in the Settings/Advanced Settings tab,
they're not notified through a new modal window of the list of invalid settings
changes.
* Extending CourseMetadata
- Previously, we only had update_from_json method in CourseMetadata.py,
and it was only validating one field every POST request.
- Now we have validate_and_update_from_json method that encapsulates the
functionality of update_from_json into a validation call
- To avoid discrepancy of validation standards between modules, validation
uses the from_json method implemented to each field in xblock.
* Different Response in advanced settings ajax requests
- After receiving a POST ajax request, course.py calls
validate_and_update_from_json, and sends a json object of either:
1) valid course metadata model
2) error objects
* Error Messages shown in validation-error-modal
- error objects passed through ajax are shown in a separate modal.
140 lines
5.5 KiB
HTML
140 lines
5.5 KiB
HTML
<%inherit file="base.html" />
|
|
<%def name="online_help_token()"><% return "advanced" %></%def>
|
|
<%namespace name='static' file='static_content.html'/>
|
|
<%!
|
|
from django.utils.translation import ugettext as _
|
|
from contentstore import utils
|
|
from django.utils.html import escapejs
|
|
%>
|
|
<%block name="title">${_("Advanced Settings")}</%block>
|
|
<%block name="bodyclass">is-signedin course advanced view-settings</%block>
|
|
|
|
<%block name="jsextra">
|
|
% for template_name in ["advanced_entry", "basic-modal", "modal-button", "validation-error-modal"]:
|
|
<script type="text/template" id="${template_name}-tpl">
|
|
<%static:include path="js/${template_name}.underscore" />
|
|
</script>
|
|
% endfor
|
|
|
|
<script type="text/javascript">
|
|
require(["domReady!", "jquery", "gettext", "js/models/settings/advanced", "js/views/settings/advanced"],
|
|
function(doc, $, gettext, AdvancedSettingsModel, AdvancedSettingsView) {
|
|
$("form :input").focus(function() {
|
|
$("label[for='" + this.id + "']").addClass("is-focused");
|
|
}).blur(function() {
|
|
$("label").removeClass("is-focused");
|
|
});
|
|
|
|
// proactively populate advanced b/c it has the filtered list and doesn't really follow the model pattern
|
|
var advancedModel = new AdvancedSettingsModel(${advanced_dict | n}, {parse: true});
|
|
advancedModel.url = "${advanced_settings_url}";
|
|
|
|
var editor = new AdvancedSettingsView({
|
|
el: $('.settings-advanced'),
|
|
model: advancedModel
|
|
});
|
|
editor.render();
|
|
|
|
$("#deprecated-settings").click(function() {
|
|
var $this = $(this);
|
|
var wrapperDeprecatedSetting = $('.wrapper-deprecated-setting');
|
|
var deprecatedSettingsLabel = $('.deprecated-settings-label');
|
|
if ($this.is(':checked')) {
|
|
wrapperDeprecatedSetting.addClass('is-set');
|
|
deprecatedSettingsLabel.text('${escapejs(_('Hide Deprecated Settings'))}');
|
|
editor.render_deprecated = true;
|
|
}
|
|
else {
|
|
wrapperDeprecatedSetting.removeClass('is-set');
|
|
deprecatedSettingsLabel.text('${escapejs(_('Show Deprecated Settings'))}');
|
|
editor.render_deprecated = false;
|
|
}
|
|
|
|
editor.render();
|
|
});
|
|
|
|
});
|
|
|
|
</script>
|
|
</%block>
|
|
|
|
<%block name="content">
|
|
<div class="wrapper-mast wrapper">
|
|
<header class="mast has-subtitle">
|
|
<h1 class="page-header">
|
|
<small class="subtitle">${_("Settings")}</small>
|
|
<span class="sr">> </span>${_("Advanced Settings")}
|
|
</h1>
|
|
</header>
|
|
</div>
|
|
|
|
<div class="wrapper-content wrapper">
|
|
<section class="content">
|
|
<article class="content-primary" role="main">
|
|
<form id="settings_advanced" class="settings-advanced" method="post" action="">
|
|
|
|
<div class="message message-status confirm">
|
|
${_("Your policy changes have been saved.")}
|
|
</div>
|
|
|
|
<div class="message message-status error">
|
|
${_("There was an error saving your information. Please see below.")}
|
|
</div>
|
|
|
|
<section class="group-settings advanced-policies">
|
|
<header>
|
|
<h2 class="title-2">${_("Manual Policy Definition")}</h2>
|
|
|
|
</header>
|
|
|
|
<p class="instructions">${_("<strong>Warning</strong>: Do not modify these policies unless you are familiar with their purpose.")}</p>
|
|
|
|
<div class="wrapper-options">
|
|
<div class="wrapper-deprecated-setting">
|
|
<input id="deprecated-settings" class="deprecated-settings-toggle" type="checkbox" name="Show Deprecated">
|
|
<label for="deprecated-settings" class="deprecated-settings-label">${_("Show Deprecated Settings")}</label>
|
|
</div>
|
|
</div>
|
|
|
|
<ul class="list-input course-advanced-policy-list enum">
|
|
|
|
</ul>
|
|
</section>
|
|
</form>
|
|
</article>
|
|
|
|
<aside class="content-supplementary" role="complimentary">
|
|
<div class="bit">
|
|
<h3 class="title-3">${_("What do advanced settings do?")}</h3>
|
|
<p>${_("Advanced settings control specific course functionality. On this page, you can edit manual policies, which are JSON-based key and value pairs that control specific course settings.")}</p>
|
|
|
|
<p>${_("Any policies you modify here override all other information you've defined elsewhere in Studio. Do not edit policies unless you are familiar with both their purpose and syntax.")}</p>
|
|
|
|
<p>${_("{em_start}Note:{em_end} When you enter strings as policy values, ensure that you use double quotation marks (") around the string. Do not use single quotation marks (').").format(em_start='<strong>', em_end="</strong>")}</p>
|
|
</div>
|
|
|
|
<div class="bit">
|
|
% if context_course:
|
|
<%
|
|
details_url = utils.reverse_course_url('settings_handler', context_course.id)
|
|
grading_url = utils.reverse_course_url('grading_handler', context_course.id)
|
|
course_team_url = utils.reverse_course_url('course_team_handler', context_course.id)
|
|
%>
|
|
<h3 class="title-3">${_("Other Course Settings")}</h3>
|
|
<nav class="nav-related">
|
|
<ul>
|
|
<li class="nav-item"><a href="${details_url}">${_("Details & Schedule")}</a></li>
|
|
<li class="nav-item"><a href="${grading_url}">${_("Grading")}</a></li>
|
|
<li class="nav-item"><a href="${course_team_url}">${_("Course Team")}</a></li>
|
|
% if "split_test" in context_course.advanced_modules:
|
|
<li class="nav-item"><a href="${utils.reverse_course_url('group_configurations_list_handler', context_course.id)}">${_("Group Configurations")}</a></li>
|
|
% endif
|
|
</ul>
|
|
</nav>
|
|
% endif
|
|
</div>
|
|
</aside>
|
|
</section>
|
|
</div>
|
|
</%block>
|