Commit 39dc8a59 by cahrens

Perform esacaping in the templates.

Use new best practices.
parent 801bb288
...@@ -857,7 +857,7 @@ define([ ...@@ -857,7 +857,7 @@ define([
beforeEach(function() { beforeEach(function() {
TemplateHelpers.installTemplate('content-group-details', true); TemplateHelpers.installTemplate('content-group-details', true);
this.model = new GroupModel({name: 'Content Group', id: 0}); this.model = new GroupModel({name: 'Content Group', id: 0, courseOutlineUrl: "CourseOutlineUrl"});
var saveableModel = new GroupConfigurationModel({ var saveableModel = new GroupConfigurationModel({
name: 'Content Group Configuration', name: 'Content Group Configuration',
...@@ -888,7 +888,7 @@ define([ ...@@ -888,7 +888,7 @@ define([
it('should hide empty usage appropriately', function() { it('should hide empty usage appropriately', function() {
this.view.$('.hide-groups').click(); this.view.$('.hide-groups').click();
assertHideEmptyUsages(this.view) assertHideEmptyUsages(this.view);
}); });
it('should show non-empty usage appropriately', function() { it('should show non-empty usage appropriately', function() {
...@@ -1001,7 +1001,7 @@ define([ ...@@ -1001,7 +1001,7 @@ define([
'content-group-editor', 'content-group-details' 'content-group-editor', 'content-group-details'
], true); ], true);
this.model = new GroupModel({name: 'Content Group', id: 0}); this.model = new GroupModel({name: 'Content Group', id: 0, courseOutlineUrl: 'CourseOutlineUrl'});
this.saveableModel = new GroupConfigurationModel({ this.saveableModel = new GroupConfigurationModel({
name: 'Content Group Configuration', name: 'Content Group Configuration',
......
...@@ -16,7 +16,8 @@ define([ ...@@ -16,7 +16,8 @@ define([
experimentsEnabled: true, experimentsEnabled: true,
experimentGroupConfigurations: new GroupConfigurationCollection({ experimentGroupConfigurations: new GroupConfigurationCollection({
id: 0, id: 0,
name: 'Configuration 1' name: 'Configuration 1',
courseOutlineUrl: "CourseOutlineUrl"
}), }),
contentGroupConfiguration: new GroupConfigurationModel({groups: []}) contentGroupConfiguration: new GroupConfigurationModel({groups: []})
}); });
......
...@@ -3,8 +3,9 @@ ...@@ -3,8 +3,9 @@
* It is expected to be backed by a Group model. * It is expected to be backed by a Group model.
*/ */
define([ define([
'js/views/baseview', 'underscore', 'gettext', 'underscore.string' 'js/views/baseview', 'underscore', 'gettext', 'underscore.string',
], function(BaseView, _, gettext, str) { 'edx-ui-toolkit/js/utils/string-utils', 'edx-ui-toolkit/js/utils/html-utils'
], function(BaseView, _, gettext, str, StringUtils, HtmlUtils) {
'use strict'; 'use strict';
var ContentGroupDetailsView = BaseView.extend({ var ContentGroupDetailsView = BaseView.extend({
...@@ -37,9 +38,10 @@ define([ ...@@ -37,9 +38,10 @@ define([
render: function(showContentGroupUsages) { render: function(showContentGroupUsages) {
var attrs = $.extend({}, this.model.attributes, { var attrs = $.extend({}, this.model.attributes, {
usageCountMessage: this.getUsageCountTitle(), usageCountMessage: this.getUsageCountTitle(),
outlineAnchorMessage: this.getOutlineAnchorMessage(), courseOutlineUrl: this.model.collection.parents[0].outlineUrl,
index: this.model.collection.indexOf(this.model), index: this.model.collection.indexOf(this.model),
showContentGroupUsages: showContentGroupUsages || false showContentGroupUsages: showContentGroupUsages || false,
HtmlUtils: HtmlUtils
}); });
this.$el.html(this.template(attrs)); this.$el.html(this.template(attrs));
return this; return this;
...@@ -56,41 +58,23 @@ define([ ...@@ -56,41 +58,23 @@ define([
}, },
getUsageCountTitle: function () { getUsageCountTitle: function () {
var count = this.model.get('usage').length, message; var count = this.model.get('usage').length;
if (count === 0) { if (count === 0) {
message = gettext('Not in Use'); return gettext('Not in Use');
} else { } else {
message = ngettext( /* globals ngettext */
return StringUtils.interpolate(ngettext(
/* /*
Translators: 'count' is number of units that the group Translators: 'count' is number of units that the group
configuration is used in. configuration is used in.
*/ */
'Used in %(count)s unit', 'Used in %(count)s units', 'Used in {count} unit', 'Used in {count} units',
count count
),
{count: count}
); );
} }
return interpolate(message, { count: count }, true);
},
getOutlineAnchorMessage: function () {
var message = _.escape(gettext(
/*
Translators: 'outlineAnchor' is an anchor pointing to
the course outline page.
*/
'This content group is not in use. Add a content group to any unit from the %(outlineAnchor)s.'
)),
anchor = str.sprintf(
'<a href="%(url)s" title="%(text)s">%(text)s</a>',
{
url: this.model.collection.parents[0].outlineUrl,
text: _.escape(gettext('Course Outline'))
}
);
return str.sprintf(message, {outlineAnchor: anchor});
} }
}); });
......
...@@ -23,8 +23,8 @@ function(ListItemEditorView, _) { ...@@ -23,8 +23,8 @@ function(ListItemEditorView, _) {
getTemplateOptions: function() { getTemplateOptions: function() {
return { return {
id: this.model.escape('id'), id: this.model.get('id'),
name: this.model.escape('name'), name: this.model.get('name'),
index: this.model.collection.indexOf(this.model), index: this.model.collection.indexOf(this.model),
isNew: this.model.isNew(), isNew: this.model.isNew(),
usage: this.model.get('usage'), usage: this.model.get('usage'),
......
...@@ -32,7 +32,7 @@ function(BaseView, _, str, gettext) { ...@@ -32,7 +32,7 @@ function(BaseView, _, str, gettext) {
index = collection.indexOf(this.model); index = collection.indexOf(this.model);
this.$el.html(this.template({ this.$el.html(this.template({
name: this.model.escape('name'), name: this.model.get('name'),
allocation: this.getAllocation(), allocation: this.getAllocation(),
index: index, index: index,
error: this.model.validationError error: this.model.validationError
......
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
* It is expected to be instantiated with a GroupConfiguration model. * It is expected to be instantiated with a GroupConfiguration model.
*/ */
define([ define([
'js/views/baseview', 'underscore', 'gettext', 'underscore.string' 'js/views/baseview', 'underscore', 'gettext', 'underscore.string',
'edx-ui-toolkit/js/utils/string-utils', 'edx-ui-toolkit/js/utils/html-utils'
], ],
function(BaseView, _, gettext, str) { function(BaseView, _, gettext, str, StringUtils, HtmlUtils) {
'use strict'; 'use strict';
var GroupConfigurationDetailsView = BaseView.extend({ var GroupConfigurationDetailsView = BaseView.extend({
tagName: 'div', tagName: 'div',
...@@ -26,7 +27,7 @@ function(BaseView, _, gettext, str) { ...@@ -26,7 +27,7 @@ function(BaseView, _, gettext, str) {
}, },
initialize: function() { initialize: function() {
this.template = _.template( this.template = HtmlUtils.template(
$('#group-configuration-details-tpl').text() $('#group-configuration-details-tpl').text()
); );
this.listenTo(this.model, 'change', this.render); this.listenTo(this.model, 'change', this.render);
...@@ -36,11 +37,10 @@ function(BaseView, _, gettext, str) { ...@@ -36,11 +37,10 @@ function(BaseView, _, gettext, str) {
var attrs = $.extend({}, this.model.attributes, { var attrs = $.extend({}, this.model.attributes, {
groupsCountMessage: this.getGroupsCountTitle(), groupsCountMessage: this.getGroupsCountTitle(),
usageCountMessage: this.getUsageCountTitle(), usageCountMessage: this.getUsageCountTitle(),
outlineAnchorMessage: this.getOutlineAnchorMessage(), courseOutlineUrl: this.model.collection.outlineUrl,
index: this.model.collection.indexOf(this.model) index: this.model.collection.indexOf(this.model)
}); });
HtmlUtils.setHtml(this.$el, this.template(attrs));
this.$el.html(this.template(attrs));
return this; return this;
}, },
...@@ -61,54 +61,37 @@ function(BaseView, _, gettext, str) { ...@@ -61,54 +61,37 @@ function(BaseView, _, gettext, str) {
getGroupsCountTitle: function () { getGroupsCountTitle: function () {
var count = this.model.get('groups').length, var count = this.model.get('groups').length,
/* globals ngettext */
message = ngettext( message = ngettext(
/* /*
Translators: 'count' is number of groups that the group Translators: 'count' is number of groups that the group
configuration contains. configuration contains.
*/ */
'Contains %(count)s group', 'Contains %(count)s groups', 'Contains {count} group', 'Contains {count} groups',
count count
); );
return interpolate(message, { count: count }, true); return StringUtils.interpolate(message, { count: count });
}, },
getUsageCountTitle: function () { getUsageCountTitle: function () {
var count = this.model.get('usage').length, message; var count = this.model.get('usage').length;
if (count === 0) { if (count === 0) {
message = gettext('Not in Use'); return gettext('Not in Use');
} else { } else {
message = ngettext( return StringUtils.interpolate(ngettext(
/* /*
Translators: 'count' is number of units that the group Translators: 'count' is number of units that the group
configuration is used in. configuration is used in.
*/ */
'Used in %(count)s unit', 'Used in %(count)s units', 'Used in {count} unit', 'Used in {count} units',
count count
);
}
return interpolate(message, { count: count }, true);
},
getOutlineAnchorMessage: function () {
var message = gettext(
/*
Translators: 'outlineAnchor' is an anchor pointing to
the course outline page.
*/
'This Group Configuration is not in use. Start by adding a content experiment to any Unit via the %(outlineAnchor)s.'
), ),
anchor = str.sprintf( {count: count}
'<a href="%(url)s" title="%(text)s">%(text)s</a>',
{
url: this.model.collection.outlineUrl,
text: gettext('Course Outline')
}
); );
}
return str.sprintf(message, {outlineAnchor: anchor});
} }
}); });
......
...@@ -51,8 +51,8 @@ function(ListItemEditorView, _, $, gettext, ExperimentGroupEditView) { ...@@ -51,8 +51,8 @@ function(ListItemEditorView, _, $, gettext, ExperimentGroupEditView) {
return { return {
id: this.model.get('id'), id: this.model.get('id'),
uniqueId: _.uniqueId(), uniqueId: _.uniqueId(),
name: this.model.escape('name'), name: this.model.get('name'),
description: this.model.escape('description'), description: this.model.get('description'),
usage: this.model.get('usage'), usage: this.model.get('usage'),
isNew: this.model.isNew() isNew: this.model.isNew()
}; };
......
<%page expression_filter="h"/>
<%inherit file="base.html" /> <%inherit file="base.html" />
<%def name="content_groups_help_token()"><% return "content_groups" %></%def> <%def name="content_groups_help_token()"><% return "content_groups" %></%def>
<%def name="experiment_group_configurations_help_token()"><% return "group_configurations" %></%def> <%def name="experiment_group_configurations_help_token()"><% return "group_configurations" %></%def>
...@@ -8,6 +9,7 @@ from django.utils.translation import ugettext as _ ...@@ -8,6 +9,7 @@ from django.utils.translation import ugettext as _
from openedx.core.djangolib.js_utils import ( from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string dump_js_escaped_json, js_escaped_string
) )
from openedx.core.djangolib.markup import Text, HTML
%> %>
<%block name="title">${_("Group Configurations")}</%block> <%block name="title">${_("Group Configurations")}</%block>
...@@ -76,7 +78,7 @@ from openedx.core.djangolib.js_utils import ( ...@@ -76,7 +78,7 @@ from openedx.core.djangolib.js_utils import (
<h3 class="title-3">${_("Content Groups")}</h3> <h3 class="title-3">${_("Content Groups")}</h3>
<p>${_("If you have cohorts enabled in your course, you can use content groups to create cohort-specific courseware. In other words, you can customize the content that particular cohorts see in your course.")}</p> <p>${_("If you have cohorts enabled in your course, you can use content groups to create cohort-specific courseware. In other words, you can customize the content that particular cohorts see in your course.")}</p>
<p>${_("Each content group that you create can be associated with one or more cohorts. In addition to course content that is intended for all students, you can designate some content as visible only to specified content groups. Only learners in the cohorts that are associated with the specified content groups see the additional content.")}</p> <p>${_("Each content group that you create can be associated with one or more cohorts. In addition to course content that is intended for all students, you can designate some content as visible only to specified content groups. Only learners in the cohorts that are associated with the specified content groups see the additional content.")}</p>
<p>${_("Click {em_start}New content group{em_end} to add a new content group. To edit the name of a content group, hover over its box and click {em_start}Edit{em_end}. You can delete a content group only if it is not in use by a unit. To delete a content group, hover over its box and click the delete icon.").format(em_start="<strong>", em_end="</strong>")}</p> <p>${Text(_("Click {em_start}New content group{em_end} to add a new content group. To edit the name of a content group, hover over its box and click {em_start}Edit{em_end}. You can delete a content group only if it is not in use by a unit. To delete a content group, hover over its box and click the delete icon.")).format(em_start=HTML("<strong>"), em_end=HTML("</strong>"))}</p>
<p><a href="${get_online_help_info(content_groups_help_token())['doc_url']}" target="_blank" class="button external-help-button">${_("Learn More")}</a></p> <p><a href="${get_online_help_info(content_groups_help_token())['doc_url']}" target="_blank" class="button external-help-button">${_("Learn More")}</a></p>
</div> </div>
</div> </div>
...@@ -85,7 +87,7 @@ from openedx.core.djangolib.js_utils import ( ...@@ -85,7 +87,7 @@ from openedx.core.djangolib.js_utils import (
<div class="experiment-groups-doc"> <div class="experiment-groups-doc">
<h3 class="title-3">${_("Experiment Group Configurations")}</h3> <h3 class="title-3">${_("Experiment Group Configurations")}</h3>
<p>${_("Use experiment group configurations if you are conducting content experiments, also known as A/B testing, in your course. Experiment group configurations define how many groups of students are in a content experiment. When you create a content experiment for a course, you select the group configuration to use.")}</p> <p>${_("Use experiment group configurations if you are conducting content experiments, also known as A/B testing, in your course. Experiment group configurations define how many groups of students are in a content experiment. When you create a content experiment for a course, you select the group configuration to use.")}</p>
<p>${_("Click {em_start}New Group Configuration{em_end} to add a new configuration. To edit a configuration, hover over its box and click {em_start}Edit{em_end}. You can delete a group configuration only if it is not in use in an experiment. To delete a configuration, hover over its box and click the delete icon.").format(em_start="<strong>", em_end="</strong>")}</p> <p>${Text(_("Click {em_start}New Group Configuration{em_end} to add a new configuration. To edit a configuration, hover over its box and click {em_start}Edit{em_end}. You can delete a group configuration only if it is not in use in an experiment. To delete a configuration, hover over its box and click the delete icon.")).format(em_start=HTML("<strong>"), em_end=HTML("</strong>"))}</p>
<p><a href="${get_online_help_info(experiment_group_configurations_help_token())['doc_url']}" target="_blank" class="button external-help-button">${_("Learn More")}</a></p> <p><a href="${get_online_help_info(experiment_group_configurations_help_token())['doc_url']}" target="_blank" class="button external-help-button">${_("Learn More")}</a></p>
</div> </div>
</div> </div>
......
...@@ -51,9 +51,16 @@ ...@@ -51,9 +51,16 @@
</ol> </ol>
<% } else { %> <% } else { %>
<p class="group-configuration-usage-text"> <p class="group-configuration-usage-text">
<!-- This contains an anchor link and therefore can't be escaped. --> <%= HtmlUtils.interpolateHtml(
<%= outlineAnchorMessage %> gettext('This content group is not in use. Add a content group to any unit from the {linkStart}Course Outline{linkEnd}.'),
{
linkStart: HtmlUtils.interpolateHtml(
HtmlUtils.HTML('<a href="{courseOutlineUrl}" title="{courseOutlineTitle}">'),
{courseOutlineUrl: courseOutlineUrl, courseOutlineTitle: gettext('Course Outline')}),
linkEnd: HtmlUtils.HTML('</a>')
})
%>
</p> </p>
<% } %> <% } %>
</div> </div>
<% } %> <% } %>
\ No newline at end of file
<form class="collection-edit-form"> <form class="collection-edit-form">
<% if (error && error.message) { %> <% if (error && error.message) { %>
<div class="content-group-edit-error message message-status message-status error is-shown"> <div class="content-group-edit-error message message-status message-status error is-shown">
<%= gettext(error.message) %> <%- gettext(error.message) %>
</div> </div>
<% } %> <% } %>
<div class="wrapper-form"> <div class="wrapper-form">
<fieldset class="collection-fields"> <fieldset class="collection-fields">
<div class="input-wrap field text required add-collection-name <% if(error && error.attributes && error.attributes.name) { print('error'); } %>"> <div class="input-wrap field text required add-collection-name <% if(error && error.attributes && error.attributes.name) { print('error'); } %>">
<label for="group-cohort-name-<%= uniqueId %>"><%= gettext("Content Group Name") %></label><% <label for="group-cohort-name-<%- uniqueId %>"><%- gettext("Content Group Name") %></label><%
if (!_.isUndefined(id) && !_.isEmpty(id)) { if (!_.isUndefined(id) && !_.isEmpty(id)) {
%><span class="group-configuration-id"> %><span class="group-configuration-id">
<span class="group-configuration-label"><%= gettext('Content Group ID') %></span> <span class="group-configuration-label"><%- gettext('Content Group ID') %></span>
<span class="group-configuration-value"><%= id %></span> <span class="group-configuration-value"><%- id %></span>
</span><% </span><%
} }
%> %>
<input name="group-cohort-name" id="group-cohort-name-<%= uniqueId %>" class="collection-name-input input-text" value="<%- name %>" type="text" placeholder="<%= gettext("This is the name of the group") %>"> <input name="group-cohort-name" id="group-cohort-name-<%- uniqueId %>" class="collection-name-input input-text" value="<%- name %>" type="text" placeholder="<%- gettext("This is the name of the group") %>">
</div> </div>
</fieldset> </fieldset>
<% if (!_.isEmpty(usage)) { %> <% if (!_.isEmpty(usage)) { %>
<div class="wrapper-group-configuration-validation usage-validation"> <div class="wrapper-group-configuration-validation usage-validation">
<i class="icon fa fa-warning"></i> <i class="icon fa fa-warning"></i>
<p class="group-configuration-validation-text"> <p class="group-configuration-validation-text">
<%= gettext('This content group is used in one or more units.') %> <%- gettext('This content group is used in one or more units.') %>
</p> </p>
</div> </div>
<% } %> <% } %>
</div> </div>
<div class="actions"> <div class="actions">
<button class="action action-primary" type="submit"><% if (isNew) { print(gettext("Create")) } else { print(gettext("Save")) } %></button> <button class="action action-primary" type="submit"><% if (isNew) { print(gettext("Create")) } else { print(gettext("Save")) } %></button>
<button class="action action-secondary action-cancel"><%= gettext("Cancel") %></button> <button class="action action-secondary action-cancel"><%- gettext("Cancel") %></button>
<% if (!isNew) { %> <% if (!isNew) { %>
<% if (_.isEmpty(usage)) { %> <% if (_.isEmpty(usage)) { %>
<span class="wrapper-delete-button" data-tooltip="<%= gettext("Delete") %>"> <span class="wrapper-delete-button" data-tooltip="<%- gettext("Delete") %>">
<a class="button action-delete delete" href="#"><%= gettext("Delete") %></a> <a class="button action-delete delete" href="#"><%- gettext("Delete") %></a>
</span> </span>
<% } else { %> <% } else { %>
<span class="wrapper-delete-button" data-tooltip="<%= gettext('Cannot delete when in use by a unit') %>"> <span class="wrapper-delete-button" data-tooltip="<%- gettext('Cannot delete when in use by a unit') %>">
<a class="button action-delete delete is-disabled" href="#" aria-disabled="true" ><%= gettext("Delete") %></a> <a class="button action-delete delete is-disabled" href="#" aria-disabled="true" ><%- gettext("Delete") %></a>
</span> </span>
<% } %> <% } %>
<% } %> <% } %>
......
...@@ -80,8 +80,15 @@ ...@@ -80,8 +80,15 @@
</ol> </ol>
<% } else { %> <% } else { %>
<p class="group-configuration-usage-text"> <p class="group-configuration-usage-text">
<!-- This contains an anchor link and therefore can't be escaped. --> <%= HtmlUtils.interpolateHtml(
<%= outlineAnchorMessage %> gettext('This Group Configuration is not in use. Start by adding a content experiment to any Unit via the {linkStart}Course Outline{linkEnd}.'),
{
linkStart: HtmlUtils.interpolateHtml(
HtmlUtils.HTML('<a href="{courseOutlineUrl}" title="{courseOutlineTitle}">'),
{ courseOutlineUrl: courseOutlineUrl, courseOutlineTitle: gettext('Course Outline')}),
linkEnd: HtmlUtils.HTML('</a>')
})
%>
</p> </p>
<% } %> <% } %>
</div> </div>
......
<form class="collection-edit-form group-configuration-edit-form"> <form class="collection-edit-form group-configuration-edit-form">
<% if (error && error.message) { %> <% if (error && error.message) { %>
<div class="group-configuration-edit-error message message-status message-status error is-shown" name="group-configuration-edit-error"> <div class="group-configuration-edit-error message message-status message-status error is-shown" name="group-configuration-edit-error">
<%= gettext(error.message) %> <%- gettext(error.message) %>
</div> </div>
<% } %> <% } %>
<div class="wrapper-form"> <div class="wrapper-form">
<fieldset class="collection-fields group-configuration-fields"> <fieldset class="collection-fields group-configuration-fields">
<legend class="sr"><%= gettext("Group Configuration information") %></legend> <legend class="sr"><%- gettext("Group Configuration information") %></legend>
<div class="input-wrap field text required add-collection-name add-group-configuration-name <% if(error && error.attributes && error.attributes.name) { print('error'); } %>"> <div class="input-wrap field text required add-collection-name add-group-configuration-name <% if(error && error.attributes && error.attributes.name) { print('error'); } %>">
<label for="group-configuration-name-<%= uniqueId %>"><%= gettext("Group Configuration Name") %></label><% <label for="group-configuration-name-<%- uniqueId %>"><%- gettext("Group Configuration Name") %></label><%
if (!_.isUndefined(id)) { if (!_.isUndefined(id)) {
%><span class="group-configuration-id"> %><span class="group-configuration-id">
<span class="group-configuration-label"><%= gettext('Group Configuration ID') %></span> <span class="group-configuration-label"><%- gettext('Group Configuration ID') %></span>
<span class="group-configuration-value"><%= id %></span> <span class="group-configuration-value"><%- id %></span>
</span><% </span><%
} }
%> %>
<input id="group-configuration-name-<%= uniqueId %>" class="collection-name-input input-text" name="group-configuration-name" type="text" placeholder="<%= gettext("This is the Name of the Group Configuration") %>" value="<%= name %>"> <input id="group-configuration-name-<%- uniqueId %>" class="collection-name-input input-text" name="group-configuration-name" type="text" placeholder="<%- gettext("This is the Name of the Group Configuration") %>" value="<%- name %>">
<span class="tip tip-stacked"><%= gettext("Name or short description of the configuration") %></span> <span class="tip tip-stacked"><%- gettext("Name or short description of the configuration") %></span>
</div> </div>
<div class="input-wrap field text add-group-configuration-description"> <div class="input-wrap field text add-group-configuration-description">
<label for="group-configuration-description-<%= uniqueId %>"><%= gettext("Description") %></label> <label for="group-configuration-description-<%- uniqueId %>"><%- gettext("Description") %></label>
<textarea id="group-configuration-description-<%= uniqueId %>" class="group-configuration-description-input text input-text" name="group-configuration-description" placeholder="<%= gettext("This is the Description of the Group Configuration") %>"><%= description %></textarea> <textarea id="group-configuration-description-<%- uniqueId %>" class="group-configuration-description-input text input-text" name="group-configuration-description" placeholder="<%- gettext("This is the Description of the Group Configuration") %>"><%- description %></textarea>
<span class="tip tip-stacked"><%= gettext("Optional long description") %></span> <span class="tip tip-stacked"><%- gettext("Optional long description") %></span>
</div> </div>
</fieldset> </fieldset>
<fieldset class="groups-fields"> <fieldset class="groups-fields">
<legend class="sr"><%= gettext("Group information") %></legend> <legend class="sr"><%- gettext("Group information") %></legend>
<label class="groups-fields-label required"><%= gettext("Groups") %></label> <label class="groups-fields-label required"><%- gettext("Groups") %></label>
<span class="tip tip-stacked"><%= gettext("Name of the groups that students will be assigned to, for example, Control, Video, Problems. You must have two or more groups.") %></span> <span class="tip tip-stacked"><%- gettext("Name of the groups that students will be assigned to, for example, Control, Video, Problems. You must have two or more groups.") %></span>
<ol class="groups list-input enum"></ol> <ol class="groups list-input enum"></ol>
<button class="action action-add-group action-add-item"><i class="icon fa fa-plus"></i> <%= gettext("Add another group") %></button> <button class="action action-add-group action-add-item"><i class="icon fa fa-plus"></i> <%- gettext("Add another group") %></button>
</fieldset> </fieldset>
<% if (!_.isEmpty(usage)) { %> <% if (!_.isEmpty(usage)) { %>
<div class="wrapper-group-configuration-validation usage-validation"> <div class="wrapper-group-configuration-validation usage-validation">
<i class="icon fa fa-warning"></i> <i class="icon fa fa-warning"></i>
<p class="group-configuration-validation-text"> <p class="group-configuration-validation-text">
<%= gettext('This configuration is currently used in content experiments. If you make changes to the groups, you may need to edit those experiments.') %> <%- gettext('This configuration is currently used in content experiments. If you make changes to the groups, you may need to edit those experiments.') %>
</p> </p>
</div> </div>
<% } %> <% } %>
</div> </div>
<div class="actions"> <div class="actions">
<button class="action action-primary" type="submit"><% if (isNew) { print(gettext("Create")) } else { print(gettext("Save")) } %></button> <button class="action action-primary" type="submit"><% if (isNew) { print(gettext("Create")) } else { print(gettext("Save")) } %></button>
<button class="action action-secondary action-cancel"><%= gettext("Cancel") %></button> <button class="action action-secondary action-cancel"><%- gettext("Cancel") %></button>
<% if (!isNew) { %> <% if (!isNew) { %>
<% if (_.isEmpty(usage)) { %> <% if (_.isEmpty(usage)) { %>
<span class="wrapper-delete-button"> <span class="wrapper-delete-button">
<a class="button action-delete delete" href="#"><%= gettext("Delete") %></a> <a class="button action-delete delete" href="#"><%- gettext("Delete") %></a>
</span> </span>
<% } else { %> <% } else { %>
<span class="wrapper-delete-button" data-tooltip="<%= gettext('Cannot delete when in use by an experiment') %>"> <span class="wrapper-delete-button" data-tooltip="<%- gettext('Cannot delete when in use by an experiment') %>">
<a class="button action-delete delete is-disabled" href="#" aria-disabled="true" ><%= gettext("Delete") %></a> <a class="button action-delete delete is-disabled" href="#" aria-disabled="true" ><%- gettext("Delete") %></a>
</span> </span>
<% } %> <% } %>
<% } %> <% } %>
......
<div class="input-wrap field long text required field-add-group-name group-<%= index %>-name <div class="input-wrap field long text required field-add-group-name group-<%- index %>-name
<% if (error && error.attributes && error.attributes.name) { print('error'); } %>"><input name="group-<%= index %>-name" class="group-name long" value="<%= name %>" type="text"> <% if (error && error.attributes && error.attributes.name) { print('error'); } %>"><input name="group-<%- index %>-name" class="group-name long" value="<%- name %>" type="text">
</div><div class="group-allocation"><%= allocation %>%</div> </div><div class="group-allocation"><%- allocation %>%</div>
<a href="" class="action action-close"><i class="icon fa fa-times-circle"></i> <span class="sr"><%= gettext("delete group") %></span></a> <a href="" class="action action-close"><i class="icon fa fa-times-circle"></i> <span class="sr"><%- gettext("delete group") %></span></a>
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<p> <p>
<%- emptyMessage %> <%- emptyMessage %>
<a href="#" class="button new-button"><i class="icon fa fa-plus"></i> <a href="#" class="button new-button"><i class="icon fa fa-plus"></i>
<%= interpolate( <%- interpolate(
gettext("%(new_item_message)s"), {new_item_message: newItemMessage}, true gettext("%(new_item_message)s"), {new_item_message: newItemMessage}, true
) %></a> ) %></a>
</p> </p>
......
;(function (define) { ;(function (define) {
'use strict'; 'use strict';
define(['jquery', 'underscore', 'backbone', 'gettext', 'js/groups/views/cohort_discussions'], define(['jquery', 'underscore', 'backbone', 'gettext', 'js/groups/views/cohort_discussions',
function ($, _, Backbone, gettext, CohortDiscussionConfigurationView) { 'edx-ui-toolkit/js/utils/html-utils'],
function ($, _, Backbone, gettext, CohortDiscussionConfigurationView, HtmlUtils) {
var CourseWideDiscussionsView = CohortDiscussionConfigurationView.extend({ var CourseWideDiscussionsView = CohortDiscussionConfigurationView.extend({
events: { events: {
'change .check-discussion-subcategory-course-wide': 'discussionCategoryStateChanged', 'change .check-discussion-subcategory-course-wide': 'discussionCategoryStateChanged',
...@@ -9,13 +10,13 @@ ...@@ -9,13 +10,13 @@
}, },
initialize: function (options) { initialize: function (options) {
this.template = _.template($('#cohort-discussions-course-wide-tpl').text()); this.template = HtmlUtils.template($('#cohort-discussions-course-wide-tpl').text());
this.cohortSettings = options.cohortSettings; this.cohortSettings = options.cohortSettings;
}, },
render: function () { render: function () {
this.$('.cohort-course-wide-discussions-nav').html(this.template({ HtmlUtils.setHtml(this.$('.cohort-course-wide-discussions-nav'), this.template({
courseWideTopics: this.getCourseWideDiscussionsHtml( courseWideTopicsHtml: this.getCourseWideDiscussionsHtml(
this.model.get('course_wide_discussions') this.model.get('course_wide_discussions')
) )
})); }));
...@@ -25,14 +26,14 @@ ...@@ -25,14 +26,14 @@
/** /**
* Returns the html list for course-wide discussion topics. * Returns the html list for course-wide discussion topics.
* @param {object} courseWideDiscussions - course-wide discussions object from server. * @param {object} courseWideDiscussions - course-wide discussions object from server.
* @returns {Array} - HTML list for course-wide discussion topics. * @returns {HtmlSnippet} - HTML list for course-wide discussion topics.
*/ */
getCourseWideDiscussionsHtml: function (courseWideDiscussions) { getCourseWideDiscussionsHtml: function (courseWideDiscussions) {
var subCategoryTemplate = _.template($('#cohort-discussions-subcategory-tpl').html()), var subCategoryTemplate = HtmlUtils.template($('#cohort-discussions-subcategory-tpl').html()),
entries = courseWideDiscussions.entries, entries = courseWideDiscussions.entries,
children = courseWideDiscussions.children; children = courseWideDiscussions.children;
return _.map(children, function (name) { return HtmlUtils.joinHtml.apply(this, _.map(children, function (name) {
var entry = entries[name]; var entry = entries[name];
return subCategoryTemplate({ return subCategoryTemplate({
name: name, name: name,
...@@ -40,7 +41,7 @@ ...@@ -40,7 +41,7 @@
is_cohorted: entry.is_cohorted, is_cohorted: entry.is_cohorted,
type: 'course-wide' type: 'course-wide'
}); });
}).join(''); }));
}, },
/** /**
......
;(function (define) { ;(function (define) {
'use strict'; 'use strict';
define(['jquery', 'underscore', 'backbone', 'gettext', 'js/groups/views/cohort_discussions', 'js/vendor/jquery.qubit'], define(['jquery', 'underscore', 'backbone', 'gettext', 'js/groups/views/cohort_discussions',
function ($, _, Backbone, gettext, CohortDiscussionConfigurationView) { 'edx-ui-toolkit/js/utils/html-utils', 'js/vendor/jquery.qubit'],
function ($, _, Backbone, gettext, CohortDiscussionConfigurationView, HtmlUtils) {
var InlineDiscussionsView = CohortDiscussionConfigurationView.extend({ var InlineDiscussionsView = CohortDiscussionConfigurationView.extend({
events: { events: {
'change .check-discussion-category': 'setSaveButton', 'change .check-discussion-category': 'setSaveButton',
...@@ -12,15 +13,16 @@ ...@@ -12,15 +13,16 @@
}, },
initialize: function (options) { initialize: function (options) {
this.template = _.template($('#cohort-discussions-inline-tpl').text()); this.template = HtmlUtils.template($('#cohort-discussions-inline-tpl').text());
this.cohortSettings = options.cohortSettings; this.cohortSettings = options.cohortSettings;
}, },
render: function () { render: function () {
var alwaysCohortInlineDiscussions = this.cohortSettings.get('always_cohort_inline_discussions'); var alwaysCohortInlineDiscussions = this.cohortSettings.get('always_cohort_inline_discussions'),
inline_discussions = this.model.get('inline_discussions');
this.$('.cohort-inline-discussions-nav').html(this.template({ HtmlUtils.setHtml(this.$('.cohort-inline-discussions-nav'), this.template({
inlineDiscussionTopics: this.getInlineDiscussionsHtml(this.model.get('inline_discussions')), inlineDiscussionTopicsHtml: this.getInlineDiscussionsHtml(inline_discussions),
alwaysCohortInlineDiscussions:alwaysCohortInlineDiscussions alwaysCohortInlineDiscussions:alwaysCohortInlineDiscussions
})); }));
...@@ -36,35 +38,35 @@ ...@@ -36,35 +38,35 @@
/** /**
* Generate html list for inline discussion topics. * Generate html list for inline discussion topics.
* @params {object} inlineDiscussions - inline discussions object from server. * @params {object} inlineDiscussions - inline discussions object from server.
* @returns {Array} - HTML for inline discussion topics. * @returns {HtmlSnippet} - HTML for inline discussion topics.
*/ */
getInlineDiscussionsHtml: function (inlineDiscussions) { getInlineDiscussionsHtml: function (inlineDiscussions) {
var categoryTemplate = _.template($('#cohort-discussions-category-tpl').html()), var categoryTemplate = HtmlUtils.template($('#cohort-discussions-category-tpl').html()),
entryTemplate = _.template($('#cohort-discussions-subcategory-tpl').html()), entryTemplate = HtmlUtils.template($('#cohort-discussions-subcategory-tpl').html()),
isCategoryCohorted = false, isCategoryCohorted = false,
children = inlineDiscussions.children, children = inlineDiscussions.children,
entries = inlineDiscussions.entries, entries = inlineDiscussions.entries,
subcategories = inlineDiscussions.subcategories; subcategories = inlineDiscussions.subcategories;
return _.map(children, function (name) { return HtmlUtils.joinHtml.apply(this, _.map(children, function (name) {
var html = '', entry; var htmlSnippet = '', entry;
if (entries && _.has(entries, name)) { if (entries && _.has(entries, name)) {
entry = entries[name]; entry = entries[name];
html = entryTemplate({ htmlSnippet = entryTemplate({
name: name, name: name,
id: entry.id, id: entry.id,
is_cohorted: entry.is_cohorted, is_cohorted: entry.is_cohorted,
type: 'inline' type: 'inline'
}); });
} else { // subcategory } else { // subcategory
html = categoryTemplate({ htmlSnippet = categoryTemplate({
name: name, name: name,
entries: this.getInlineDiscussionsHtml(subcategories[name]), entriesHtml: this.getInlineDiscussionsHtml(subcategories[name]),
isCategoryCohorted: isCategoryCohorted isCategoryCohorted: isCategoryCohorted
}); });
} }
return html; return htmlSnippet;
}, this).join(''); }, this));
}, },
/** /**
......
;(function (define) { ;(function (define) {
'use strict'; 'use strict';
define(['jquery', 'underscore', 'backbone', 'gettext', 'js/groups/models/cohort', define(['jquery', 'underscore', 'backbone', 'gettext', 'edx-ui-toolkit/js/utils/html-utils',
'js/models/notification', 'js/views/notification'], 'js/models/notification', 'js/views/notification'],
function($, _, Backbone, gettext, CohortModel) { function($, _, Backbone, gettext, HtmlUtils) {
var CohortFormView = Backbone.View.extend({ var CohortFormView = Backbone.View.extend({
events : { events : {
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
}, },
initialize: function(options) { initialize: function(options) {
this.template = _.template($('#cohort-form-tpl').text()); this.template = HtmlUtils.template($('#cohort-form-tpl').text());
this.contentGroups = options.contentGroups; this.contentGroups = options.contentGroups;
this.context = options.context; this.context = options.context;
}, },
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
}, },
render: function() { render: function() {
this.$el.html(this.template({ HtmlUtils.setHtml(this.$el, this.template({
cohort: this.model, cohort: this.model,
isDefaultCohort: this.isDefault(this.model.get('name')), isDefaultCohort: this.isDefault(this.model.get('name')),
contentGroups: this.contentGroups, contentGroups: this.contentGroups,
......
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
'js/groups/views/cohort_editor', 'js/groups/views/cohort_form', 'js/groups/views/cohort_editor', 'js/groups/views/cohort_form',
'js/groups/views/course_cohort_settings_notification', 'js/groups/views/course_cohort_settings_notification',
'js/groups/views/cohort_discussions_inline', 'js/groups/views/cohort_discussions_course_wide', 'js/groups/views/cohort_discussions_inline', 'js/groups/views/cohort_discussions_course_wide',
'edx-ui-toolkit/js/utils/html-utils',
'js/views/file_uploader', 'js/models/notification', 'js/views/notification', 'string_utils'], 'js/views/file_uploader', 'js/models/notification', 'js/views/notification', 'string_utils'],
function($, _, Backbone, gettext, CohortModel, CohortEditorView, CohortFormView, function($, _, Backbone, gettext, CohortModel, CohortEditorView, CohortFormView,
CourseCohortSettingsNotificationView, InlineDiscussionsView, CourseWideDiscussionsView) { CourseCohortSettingsNotificationView, InlineDiscussionsView, CourseWideDiscussionsView, HtmlUtils) {
var hiddenClass = 'is-hidden', var hiddenClass = 'is-hidden',
disabledClass = 'is-disabled'; disabledClass = 'is-disabled';
...@@ -27,8 +28,8 @@ ...@@ -27,8 +28,8 @@
initialize: function(options) { initialize: function(options) {
var model = this.model; var model = this.model;
this.template = _.template($('#cohorts-tpl').text()); this.template = HtmlUtils.template($('#cohorts-tpl').text());
this.selectorTemplate = _.template($('#cohort-selector-tpl').text()); this.selectorTemplate = HtmlUtils.template($('#cohort-selector-tpl').text());
this.context = options.context; this.context = options.context;
this.contentGroups = options.contentGroups; this.contentGroups = options.contentGroups;
this.cohortSettings = options.cohortSettings; this.cohortSettings = options.cohortSettings;
...@@ -43,7 +44,7 @@ ...@@ -43,7 +44,7 @@
}, },
render: function() { render: function() {
this.$el.html(this.template({ HtmlUtils.setHtml(this.$el, this.template({
cohorts: this.model.models, cohorts: this.model.models,
cohortsEnabled: this.cohortSettings.get('is_cohorted') cohortsEnabled: this.cohortSettings.get('is_cohorted')
})); }));
...@@ -52,7 +53,7 @@ ...@@ -52,7 +53,7 @@
}, },
renderSelector: function(selectedCohort) { renderSelector: function(selectedCohort) {
this.$('.cohort-select').html(this.selectorTemplate({ HtmlUtils.setHtml(this.$('.cohort-select'), this.selectorTemplate({
cohorts: this.model.models, cohorts: this.model.models,
selectedCohort: selectedCohort selectedCohort: selectedCohort
})); }));
......
...@@ -6,5 +6,5 @@ ...@@ -6,5 +6,5 @@
</label> </label>
</div> </div>
<ul class="wrapper-tabs subcategories"><%= entries %></ul> <ul class="wrapper-tabs subcategories"><%= HtmlUtils.ensureHtml(entriesHtml) %></ul>
</li> </li>
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<h3 class="subsection-title"><%- gettext('Course-Wide Discussion Topics') %></h3> <h3 class="subsection-title"><%- gettext('Course-Wide Discussion Topics') %></h3>
<p><%- gettext('Select the course-wide discussion topics that you want to divide by cohort.') %></p> <p><%- gettext('Select the course-wide discussion topics that you want to divide by cohort.') %></p>
<div class="field"> <div class="field">
<ul class="discussions-wrapper"><%= courseWideTopics %></ul> <ul class="discussions-wrapper"><%= HtmlUtils.ensureHtml(courseWideTopicsHtml) %></ul>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
</div> </div>
<hr class="divider divider-lv1" /> <hr class="divider divider-lv1" />
<div class="field"> <div class="field">
<% if ( inlineDiscussionTopics ) { %> <% if ( inlineDiscussionTopicsHtml.valueOf() ) { %>
<ul class="inline-topics discussions-wrapper"><%= inlineDiscussionTopics %></ul> <ul class="inline-topics discussions-wrapper"><%= HtmlUtils.ensureHtml(inlineDiscussionTopicsHtml) %></ul>
<% } else { %> <% } else { %>
<span class="no-topics"><%- gettext('No content-specific discussion topics exist.') %></span> <span class="no-topics"><%- gettext('No content-specific discussion topics exist.') %></span>
<% } %> <% } %>
......
...@@ -93,14 +93,13 @@ ...@@ -93,14 +93,13 @@
<p class="copy-error"> <p class="copy-error">
<i class="icon fa fa-exclamation-triangle" aria-hidden="true"></i> <i class="icon fa fa-exclamation-triangle" aria-hidden="true"></i>
<%= <%=
interpolate( HtmlUtils.interpolateHtml(
// Translators: Any text between %(screen_reader_start)s and %(screen_reader_end)s is only read by screen readers and never shown in the browser. // Translators: Any text between {screen_reader_start} and {screen_reader_end} is only read by screen readers and never shown in the browser.
gettext('%(screen_reader_start)sWarning:%(screen_reader_end)s The previously selected content group was deleted. Select another content group.'), gettext('{screen_reader_start}Warning:{screen_reader_end} The previously selected content group was deleted. Select another content group.'),
{ {
screen_reader_start: '<span class="sr">', screen_reader_start: HtmlUtils.HTML('<span class="sr">'),
screen_reader_end: '</span>' screen_reader_end: HtmlUtils.HTML('</span>')
}, }
true
) )
%> %>
</p> </p>
...@@ -113,14 +112,13 @@ ...@@ -113,14 +112,13 @@
<p class="copy-error"> <p class="copy-error">
<i class="icon fa fa-exclamation-triangle" aria-hidden="true"></i> <i class="icon fa fa-exclamation-triangle" aria-hidden="true"></i>
<%= <%=
interpolate( HtmlUtils.interpolateHtml(
// Translators: Any text between %(screen_reader_start)s and %(screen_reader_end)s is only read by screen readers and never shown in the browser. // Translators: Any text between {screen_reader_start} and {screen_reader_end} is only read by screen readers and never shown in the browser.
gettext('%(screen_reader_start)sWarning:%(screen_reader_end)s No content groups exist.'), gettext('{screen_reader_start}Warning:{screen_reader_end} No content groups exist.'),
{ {
screen_reader_start: '<span class="sr">', screen_reader_start: HtmlUtils.HTML('<span class="sr">'),
screen_reader_end: '</span>' screen_reader_end: HtmlUtils.HTML('</span>')
}, }
true
) )
%> %>
<a class="link-to-group-settings" href="<%- studioGroupConfigurationsUrl %>"><%- gettext("Create a content group") %></a> <a class="link-to-group-settings" href="<%- studioGroupConfigurationsUrl %>"><%- gettext("Create a content group") %></a>
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
<div class="setup-value"> <div class="setup-value">
<% if (cohort.get('assignment_type') == "manual") { %> <% if (cohort.get('assignment_type') == "manual") { %>
<%- gettext("Learners are added to this cohort only when you provide their email addresses or usernames on this page.") %> <%- gettext("Learners are added to this cohort only when you provide their email addresses or usernames on this page.") %>
<a href="http://edx.readthedocs.org/projects/edx-partner-course-staff/en/latest/course_features/cohorts/cohort_config.html#assign-learners-to-cohorts-manually" class="incontext-help action-secondary action-help" target="_blank"><%= gettext("What does this mean?") %></a> <a href="http://edx.readthedocs.org/projects/edx-partner-course-staff/en/latest/course_features/cohorts/cohort_config.html#assign-learners-to-cohorts-manually" class="incontext-help action-secondary action-help" target="_blank"><%- gettext("What does this mean?") %></a>
<% } else { %> <% } else { %>
<%- gettext("Learners are added to this cohort automatically.") %> <%- gettext("Learners are added to this cohort automatically.") %>
<a href="http://edx.readthedocs.org/projects/edx-partner-course-staff/en/latest/course_features/cohorts/cohorts_overview.html#all-automated-assignment" class="incontext-help action-secondary action-help" target="_blank"><%- gettext("What does this mean?") %></a> <a href="http://edx.readthedocs.org/projects/edx-partner-course-staff/en/latest/course_features/cohorts/cohorts_overview.html#all-automated-assignment" class="incontext-help action-secondary action-help" target="_blank"><%- gettext("What does this mean?") %></a>
......
...@@ -10,5 +10,5 @@ ...@@ -10,5 +10,5 @@
); );
var isSelected = selectedCohort && selectedCohort.get('id') === cohort.get('id') var isSelected = selectedCohort && selectedCohort.get('id') === cohort.get('id')
%> %>
<option value="<%- cohort.get('id') %>" <%= isSelected ? 'selected' : '' %>><%- label %></option> <option value="<%- cohort.get('id') %>" <%- isSelected ? 'selected' : '' %>><%- label %></option>
<% }); %> <% }); %>
<%page expression_filter="h"/>
<%page args="section_data"/> <%page args="section_data"/>
<%namespace name='static' file='../../static_content.html'/> <%namespace name='static' file='../../static_content.html'/>
<%! <%!
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from openedx.core.djangolib.js_utils import js_escaped_string from openedx.core.djangolib.js_utils import js_escaped_string, dump_js_escaped_json
from courseware.courses import get_studio_url from courseware.courses import get_studio_url
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
%> %>
...@@ -23,17 +25,17 @@ from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_ ...@@ -23,17 +25,17 @@ from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_
cohorted_user_partition = get_cohorted_user_partition(course) cohorted_user_partition = get_cohorted_user_partition(course)
content_groups = cohorted_user_partition.groups if cohorted_user_partition else [] content_groups = cohorted_user_partition.groups if cohorted_user_partition else []
%> %>
var cohortUserPartitionId = ${cohorted_user_partition.id if cohorted_user_partition else 'null'}, var cohortUserPartitionId = ${cohorted_user_partition.id if cohorted_user_partition else None | n, dump_js_escaped_json},
contentGroups = [ contentGroups = [
% for content_group in content_groups: % for content_group in content_groups:
{ {
id: ${content_group.id}, id: ${content_group.id | n, dump_js_escaped_json},
name: "${content_group.name | n, js_escaped_string}", name: "${content_group.name | n, js_escaped_string}",
user_partition_id: cohortUserPartitionId user_partition_id: cohortUserPartitionId
}, },
% endfor % endfor
]; ];
CohortsFactory(contentGroups, '${get_studio_url(course, 'group_configurations') | h}'); CohortsFactory(contentGroups, "${get_studio_url(course, 'group_configurations') | n, js_escaped_string}");
</%static:require_module> </%static:require_module>
</%block> </%block>
<div class="cohort-state-message"></div> <div class="cohort-state-message"></div>
...@@ -40,11 +40,13 @@ ...@@ -40,11 +40,13 @@
<div class="cohort-management-supplemental"> <div class="cohort-management-supplemental">
<p class=""> <p class="">
<i class="icon fa fa-info-circle" aria-hidden="true"></i> <i class="icon fa fa-info-circle" aria-hidden="true"></i>
<%= interpolate( <%= HtmlUtils.interpolateHtml(
gettext('To review student cohort assignments or see the results of uploading a CSV file, download course profile information or cohort results on %(link_start)s the Data Download page. %(link_end)s'), gettext('To review student cohort assignments or see the results of uploading a CSV file, download course profile information or cohort results on {link_start} the Data Download page. {link_end}'),
{link_start: '<a href="" class="link-cross-reference" data-section="data_download">', link_end: '</a>'}, {
true link_start: HtmlUtils.HTML('<a href="" class="link-cross-reference" data-section="data_download">'),
) %> link_end: HtmlUtils.HTML('</a>')
})
%>
</p> </p>
</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