From 53e1dfaa99f41f3c8d2bd37d391e5407ca09449f Mon Sep 17 00:00:00 2001
From: Nimisha Asthagiri
Date: Thu, 19 Oct 2017 10:09:57 -0400
Subject: [PATCH] Studio support of Section Highlights
---
cms/djangoapps/contentstore/views/item.py | 5 ++
cms/envs/common.py | 5 +-
cms/envs/test.py | 2 +
cms/static/js/models/xblock_info.js | 7 +-
cms/static/js/views/course_outline.js | 17 ++++
cms/static/js/views/modals/base_modal.js | 2 +-
.../js/views/modals/course_outline_modals.js | 90 ++++++++++++++++++-
cms/static/sass/views/_outline.scss | 17 ++++
cms/templates/course_outline.html | 2 +-
cms/templates/js/course-outline.underscore | 16 ++++
cms/templates/js/highlights-editor.underscore | 27 ++++++
11 files changed, 184 insertions(+), 6 deletions(-)
create mode 100644 cms/templates/js/highlights-editor.underscore
diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py
index 6b4cce7534..36754815f9 100644
--- a/cms/djangoapps/contentstore/views/item.py
+++ b/cms/djangoapps/contentstore/views/item.py
@@ -1182,6 +1182,11 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
xblock_info.update({
'hide_after_due': xblock.hide_after_due,
})
+ elif xblock.category == 'chapter':
+ xblock_info.update({
+ 'highlights': xblock.highlights,
+ 'highlights_enabled': settings.FEATURES.get('ENABLE_SECTION_HIGHLIGHTS', False),
+ })
# update xblock_info with special exam information if the feature flag is enabled
if settings.FEATURES.get('ENABLE_SPECIAL_EXAMS'):
diff --git a/cms/envs/common.py b/cms/envs/common.py
index 4a2d4172ef..c7e3731d55 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -264,7 +264,10 @@ FEATURES = {
# Whether archived courses (courses with end dates in the past) should be
# shown in Studio in a separate list.
- 'ENABLE_SEPARATE_ARCHIVED_COURSES': True
+ 'ENABLE_SEPARATE_ARCHIVED_COURSES': True,
+
+ # Whether section-level highlights are enabled on the platform.
+ 'ENABLE_SECTION_HIGHLIGHTS': False,
}
ENABLE_JASMINE = False
diff --git a/cms/envs/test.py b/cms/envs/test.py
index 051b10c293..cc844bfc06 100644
--- a/cms/envs/test.py
+++ b/cms/envs/test.py
@@ -326,6 +326,8 @@ SEARCH_ENGINE = "search.tests.mock_search_engine.MockSearchEngine"
FEATURES['ENABLE_ENROLLMENT_TRACK_USER_PARTITION'] = True
+FEATURES['ENABLE_SECTION_HIGHLIGHTS'] = True
+
########################## AUTHOR PERMISSION #######################
FEATURES['ENABLE_CREATOR_GROUP'] = False
diff --git a/cms/static/js/models/xblock_info.js b/cms/static/js/models/xblock_info.js
index e55bd2c4d9..84264750aa 100644
--- a/cms/static/js/models/xblock_info.js
+++ b/cms/static/js/models/xblock_info.js
@@ -159,7 +159,12 @@ function(Backbone, _, str, ModuleUtils) {
* some additional fields that are not stored in the course descriptor
* (for example, which groups are selected for this particular XBlock).
*/
- user_partitions: null
+ user_partitions: null,
+ /**
+ * This xBlock's Highlights to message to learners.
+ */
+ highlights: null,
+ highlights_enabled: null
},
initialize: function() {
diff --git a/cms/static/js/views/course_outline.js b/cms/static/js/views/course_outline.js
index d15609a7f6..3872aa2bca 100644
--- a/cms/static/js/views/course_outline.js
+++ b/cms/static/js/views/course_outline.js
@@ -197,6 +197,19 @@ define(['jquery', 'underscore', 'js/views/xblock_outline', 'common/js/components
}
},
+ highlightsXBlock: function() {
+ var modal = CourseOutlineModalsFactory.getModal('highlights', this.model, {
+ onSave: this.refresh.bind(this),
+ xblockType: XBlockViewUtils.getXBlockType(
+ this.model.get('category'), this.parentView.model, true
+ )
+ });
+
+ if (modal) {
+ modal.show();
+ }
+ },
+
addButtonActions: function(element) {
XBlockOutlineView.prototype.addButtonActions.apply(this, arguments);
element.find('.configure-button').click(function(event) {
@@ -207,6 +220,10 @@ define(['jquery', 'underscore', 'js/views/xblock_outline', 'common/js/components
event.preventDefault();
this.publishXBlock();
}.bind(this));
+ element.find('.highlights-button').click(function(event) {
+ event.preventDefault();
+ this.highlightsXBlock();
+ }.bind(this));
},
makeContentDraggable: function(element) {
diff --git a/cms/static/js/views/modals/base_modal.js b/cms/static/js/views/modals/base_modal.js
index 2974a7720a..24f703a202 100644
--- a/cms/static/js/views/modals/base_modal.js
+++ b/cms/static/js/views/modals/base_modal.js
@@ -5,7 +5,7 @@
*
* getTitle():
* returns the title for the modal.
- * getHTMLContent():
+ * getContentHtml():
* returns the HTML content to be shown inside the modal.
*
* A modal implementation should also provide the following options:
diff --git a/cms/static/js/views/modals/course_outline_modals.js b/cms/static/js/views/modals/course_outline_modals.js
index 90a0b5a028..3711e0d78f 100644
--- a/cms/static/js/views/modals/course_outline_modals.js
+++ b/cms/static/js/views/modals/course_outline_modals.js
@@ -13,10 +13,11 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
$, Backbone, _, gettext, BaseView, BaseModal, date, XBlockViewUtils, DateUtils, HtmlUtils, StringUtils
) {
'use strict';
- var CourseOutlineXBlockModal, SettingsXBlockModal, PublishXBlockModal, AbstractEditor, BaseDateEditor,
+ var CourseOutlineXBlockModal, SettingsXBlockModal, PublishXBlockModal, HighlightsXBlockModal,
+ AbstractEditor, BaseDateEditor,
ReleaseDateEditor, DueDateEditor, GradingEditor, PublishEditor, AbstractVisibilityEditor,
StaffLockEditor, UnitAccessEditor, ContentVisibilityEditor, TimedExaminationPreferenceEditor,
- AccessEditor, ShowCorrectnessEditor;
+ AccessEditor, ShowCorrectnessEditor, HighlightsEditor;
CourseOutlineXBlockModal = BaseModal.extend({
events: _.extend({}, BaseModal.prototype.events, {
@@ -206,6 +207,38 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
}
});
+ HighlightsXBlockModal = CourseOutlineXBlockModal.extend({
+ initialize: function() {
+ CourseOutlineXBlockModal.prototype.initialize.call(this);
+ if (this.options.xblockType) {
+ this.options.modalName = 'highlights-' + this.options.xblockType;
+ }
+ },
+
+ getTitle: function() {
+ return StringUtils.interpolate(
+ gettext('Highlights for {display_name}'),
+ {display_name: this.model.get('display_name')}
+ );
+ },
+
+ getIntroductionMessage: function() {
+ return StringUtils.interpolate(
+ gettext(
+ 'The highlights you provide here are messaged (i.e., emailed) to learners. Each {item}\'s ' +
+ 'highlights are emailed at the time that we expect the learner to start working on that {item}. ' +
+ 'At this time, we assume that each {item} will take 1 week to complete.'
+ ),
+ {item: this.options.xblockType}
+ );
+ },
+
+ addActionButtons: function() {
+ this.addActionButton('save', gettext('Save'), true);
+ this.addActionButton('cancel', gettext('Cancel'));
+ }
+ });
+
AbstractEditor = BaseView.extend({
tagName: 'section',
templateName: null,
@@ -844,12 +877,58 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
}
});
+ HighlightsEditor = AbstractEditor.extend({
+ templateName: 'highlights-editor',
+ className: 'edit-show-highlights',
+
+ currentValue: function() {
+ var highlights = [];
+ $('.highlight-input-text').each(function() {
+ var value = $(this).val();
+ if (value !== '' && value !== null) {
+ highlights.push(value);
+ }
+ });
+ return highlights;
+ },
+
+ hasChanges: function() {
+ return this.model.get('highlights') !== this.currentValue();
+ },
+
+ getRequestData: function() {
+ if (this.hasChanges()) {
+ return {
+ publish: 'republish',
+ metadata: {
+ highlights: this.currentValue()
+ }
+ };
+ } else {
+ return {};
+ }
+ },
+ getContext: function() {
+ return $.extend(
+ {},
+ AbstractEditor.prototype.getContext.call(this),
+ {
+ highlights: this.model.get('highlights') || []
+ }
+ );
+ }
+ });
+
return {
getModal: function(type, xblockInfo, options) {
if (type === 'edit') {
return this.getEditModal(xblockInfo, options);
} else if (type === 'publish') {
return this.getPublishModal(xblockInfo, options);
+ } else if (type === 'highlights') {
+ return this.getHighlightsModal(xblockInfo, options);
+ } else {
+ return null;
}
},
@@ -918,6 +997,13 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
editors: [PublishEditor],
model: xblockInfo
}, options));
+ },
+
+ getHighlightsModal: function(xblockInfo, options) {
+ return new HighlightsXBlockModal($.extend({
+ editors: [HighlightsEditor],
+ model: xblockInfo
+ }, options));
}
};
});
diff --git a/cms/static/sass/views/_outline.scss b/cms/static/sass/views/_outline.scss
index 926d650f75..dcbb5b8ec6 100644
--- a/cms/static/sass/views/_outline.scss
+++ b/cms/static/sass/views/_outline.scss
@@ -634,6 +634,23 @@
}
}
+ // outline: highlight settings
+ .highlights-button {
+ cursor: pointer;
+ color: $uxpl-blue-base;
+ }
+
+ .highlight-input-text {
+ width: 100%;
+ margin-bottom: 5px;
+ margin-top: 5px;
+ }
+
+ .highlights-description {
+ font-size: 80%;
+ font-weight: bolder;
+ }
+
// outline: edit item settings
.wrapper-modal-window-bulkpublish-section,
.wrapper-modal-window-bulkpublish-subsection,
diff --git a/cms/templates/course_outline.html b/cms/templates/course_outline.html
index ca0a529d44..8073e7a814 100644
--- a/cms/templates/course_outline.html
+++ b/cms/templates/course_outline.html
@@ -26,7 +26,7 @@ from openedx.core.djangolib.markup import HTML, Text
<%block name="header_extras">
-% for template_name in ['course-outline', 'xblock-string-field-editor', 'basic-modal', 'modal-button', 'course-outline-modal', 'due-date-editor', 'release-date-editor', 'grading-editor', 'publish-editor', 'staff-lock-editor', 'unit-access-editor', 'content-visibility-editor', 'verification-access-editor', 'timed-examination-preference-editor', 'access-editor', 'settings-modal-tabs', 'show-correctness-editor']:
+% for template_name in ['course-outline', 'xblock-string-field-editor', 'basic-modal', 'modal-button', 'course-outline-modal', 'due-date-editor', 'release-date-editor', 'grading-editor', 'publish-editor', 'staff-lock-editor', 'unit-access-editor', 'content-visibility-editor', 'verification-access-editor', 'timed-examination-preference-editor', 'access-editor', 'settings-modal-tabs', 'show-correctness-editor', 'highlights-editor']:
diff --git a/cms/templates/js/course-outline.underscore b/cms/templates/js/course-outline.underscore
index cb14c9d52e..30a3506920 100644
--- a/cms/templates/js/course-outline.underscore
+++ b/cms/templates/js/course-outline.underscore
@@ -200,6 +200,22 @@ if (is_proctored_exam) {
<% } %>
+ <% if (xblockInfo.get('highlights_enabled') && course.get('self_paced') && xblockInfo.isChapter()) { %>
+
+ <%- gettext('Highlights:') %>
+ <% var number_of_highlights = (xblockInfo.get('highlights') || []).length; %>
+ <% if (number_of_highlights > 0) { %>
+
+ <%- edx.StringUtils.interpolate(
+ gettext('Section Highlights: {number_of_highlights} entered'),
+ {number_of_highlights: number_of_highlights}
+ ) %>
+
+ <% } else { %>
+ <%- gettext('Enter Section Highlights') %>
+ <% } %>
+
+ <% } %>
<% if (xblockInfo.get('is_time_limited')) { %>
diff --git a/cms/templates/js/highlights-editor.underscore b/cms/templates/js/highlights-editor.underscore
new file mode 100644
index 0000000000..a8c2e97b88
--- /dev/null
+++ b/cms/templates/js/highlights-editor.underscore
@@ -0,0 +1,27 @@
+