Commit 9f74e705 by Tyler Hallada

Course Highlights Setting in Studio Course Outline

fixup! trying to "fix" underscore syntax error

add cancel modal test

Fix quality issues.

Apply Nimisha's suggestions for my quality fixes.

Skip test_drop_unit_in_collapsed_subsection

Fix quality error
parent 13edb0b0
......@@ -1188,11 +1188,17 @@ 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':
elif xblock.category in ('chapter', 'course'):
if xblock.category == 'chapter':
xblock_info.update({
'highlights': xblock.highlights,
})
elif xblock.category == 'course':
xblock_info.update({
'highlights_enabled_for_messaging': course.highlights_enabled_for_messaging,
})
xblock_info.update({
'highlights': xblock.highlights,
'highlights_enabled': highlights_setting.is_enabled(),
'highlights_enabled_for_messaging': course.highlights_enabled_for_messaging,
'highlights_preview_only': not COURSE_UPDATE_WAFFLE_FLAG.is_enabled(course.id),
'highlights_doc_url': HelpUrlExpert.the_one().url_for_token('content_highlights'),
})
......
......@@ -2577,8 +2577,10 @@ class TestXBlockInfo(ItemTest):
self.store.update_item(self.course, None)
chapter = self.store.get_item(self.chapter.location)
with highlights_setting.override():
xblock_info = create_xblock_info(chapter)
self.assertTrue(xblock_info['highlights_enabled'])
chapter_xblock_info = create_xblock_info(chapter)
course_xblock_info = create_xblock_info(self.course)
self.assertTrue(chapter_xblock_info['highlights_enabled'])
self.assertTrue(course_xblock_info['highlights_enabled_for_messaging'])
def validate_course_xblock_info(self, xblock_info, has_child_info=True, course_outline=False):
"""
......@@ -2588,6 +2590,7 @@ class TestXBlockInfo(ItemTest):
self.assertEqual(xblock_info['id'], unicode(self.course.location))
self.assertEqual(xblock_info['display_name'], self.course.display_name)
self.assertTrue(xblock_info['published'])
self.assertFalse(xblock_info['highlights_enabled_for_messaging'])
# Finally, validate the entire response for consistency
self.validate_xblock_info_consistency(xblock_info, has_child_info=has_child_info, course_outline=course_outline)
......@@ -2608,7 +2611,6 @@ class TestXBlockInfo(ItemTest):
self.assertEqual(xblock_info['format'], None)
self.assertEqual(xblock_info['highlights'], self.chapter.highlights)
self.assertFalse(xblock_info['highlights_enabled'])
self.assertFalse(xblock_info['highlights_enabled_for_messaging'])
# Finally, validate the entire response for consistency
self.validate_xblock_info_consistency(xblock_info, has_child_info=has_child_info)
......
......@@ -165,6 +165,7 @@ function(Backbone, _, str, ModuleUtils) {
*/
highlights: [],
highlights_enabled: false,
highlights_enabled_for_messaging: false,
highlights_preview_only: true,
highlights_doc_url: ''
},
......
define([
'jquery', 'underscore', 'backbone', 'js/views/utils/xblock_utils', 'js/utils/templates',
'js/views/modals/course_outline_modals', 'edx-ui-toolkit/js/utils/html-utils'],
function(
$, _, Backbone, XBlockViewUtils, TemplateUtils, CourseOutlineModalsFactory, HtmlUtils
) {
'use strict';
var CourseHighlightsEnableView = Backbone.View.extend({
events: {
'click button.status-highlights-enabled-value': 'handleEnableButtonPress',
'keypress button.status-highlights-enabled-value': 'handleEnableButtonPress'
},
initialize: function() {
this.template = TemplateUtils.loadTemplate('course-highlights-enable');
},
handleEnableButtonPress: function(event) {
if (event.type === 'click' || event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
this.highlightsEnableXBlock();
}
},
highlightsEnableXBlock: function() {
var modal = CourseOutlineModalsFactory.getModal('highlights_enable', this.model, {
onSave: this.refresh.bind(this),
xblockType: XBlockViewUtils.getXBlockType(
this.model.get('category')
)
});
if (modal) {
window.analytics.track('edx.bi.highlights_enable.modal_open');
modal.show();
}
},
refresh: function() {
this.model.fetch({
success: this.render.bind(this)
});
},
render: function() {
var html = this.template(this.model.attributes);
HtmlUtils.setHtml(this.$el, HtmlUtils.HTML(html));
return this;
}
});
return CourseHighlightsEnableView;
}
);
......@@ -17,7 +17,7 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
AbstractEditor, BaseDateEditor,
ReleaseDateEditor, DueDateEditor, GradingEditor, PublishEditor, AbstractVisibilityEditor,
StaffLockEditor, UnitAccessEditor, ContentVisibilityEditor, TimedExaminationPreferenceEditor,
AccessEditor, ShowCorrectnessEditor, HighlightsEditor;
AccessEditor, ShowCorrectnessEditor, HighlightsEditor, HighlightsEnableXBlockModal, HighlightsEnableEditor;
CourseOutlineXBlockModal = BaseModal.extend({
events: _.extend({}, BaseModal.prototype.events, {
......@@ -242,7 +242,11 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
callAnalytics: function(event) {
event.preventDefault();
window.analytics.track('edx.bi.highlights.' + event.target.innerText.toLowerCase());
this.save(event);
if (event.target.className.indexOf('save') !== -1) {
this.save(event);
} else {
this.hide();
}
},
addActionButtons: function() {
......@@ -251,6 +255,44 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
}
});
HighlightsEnableXBlockModal = CourseOutlineXBlockModal.extend({
events: _.extend({}, CourseOutlineXBlockModal.prototype.events, {
'click .action-save': 'callAnalytics',
'click .action-cancel': 'callAnalytics'
}),
initialize: function() {
CourseOutlineXBlockModal.prototype.initialize.call(this);
if (this.options.xblockType) {
this.options.modalName = 'highlights-enable-' + this.options.xblockType;
}
},
getTitle: function() {
return gettext('Enable Weekly Course Highlight Messages');
},
getIntroductionMessage: function() {
return '';
},
callAnalytics: function(event) {
event.preventDefault();
window.analytics.track('edx.bi.highlights_enable.' + event.target.innerText.toLowerCase());
if (event.target.className.indexOf('save') !== -1) {
this.save(event);
} else {
this.hide();
}
},
addActionButtons: function() {
this.addActionButton('save', gettext('Enable'), true);
this.addActionButton('cancel', gettext('Not yet'));
}
});
AbstractEditor = BaseView.extend({
tagName: 'section',
templateName: null,
......@@ -933,6 +975,42 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
}
});
HighlightsEnableEditor = AbstractEditor.extend({
templateName: 'highlights-enable-editor',
className: 'edit-enable-highlights',
currentValue: function() {
return true;
},
hasChanges: function() {
return this.model.get('highlights_enabled_for_messaging') !== this.currentValue();
},
getRequestData: function() {
if (this.hasChanges()) {
return {
publish: 'republish',
metadata: {
highlights_enabled_for_messaging: this.currentValue()
}
};
} else {
return {};
}
},
getContext: function() {
return $.extend(
{},
AbstractEditor.prototype.getContext.call(this),
{
highlights_enabled: this.model.get('highlights_enabled_for_messaging'),
highlights_doc_url: this.model.get('highlights_doc_url')
}
);
}
});
return {
getModal: function(type, xblockInfo, options) {
if (type === 'edit') {
......@@ -941,6 +1019,8 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
return this.getPublishModal(xblockInfo, options);
} else if (type === 'highlights') {
return this.getHighlightsModal(xblockInfo, options);
} else if (type === 'highlights_enable') {
return this.getHighlightsEnableModal(xblockInfo, options);
} else {
return null;
}
......@@ -1018,6 +1098,13 @@ define(['jquery', 'backbone', 'underscore', 'gettext', 'js/views/baseview',
editors: [HighlightsEditor],
model: xblockInfo
}, options));
},
getHighlightsEnableModal: function(xblockInfo, options) {
return new HighlightsEnableXBlockModal($.extend({
editors: [HighlightsEnableEditor],
model: xblockInfo
}, options));
}
};
});
/**
* This page is used to show the user an outline of the course.
*/
define(['jquery', 'underscore', 'gettext', 'js/views/pages/base_page', 'js/views/utils/xblock_utils',
'js/views/course_outline', 'common/js/components/utils/view_utils', 'common/js/components/views/feedback_alert',
'common/js/components/views/feedback_notification'],
function($, _, gettext, BasePage, XBlockViewUtils, CourseOutlineView, ViewUtils, AlertView, NoteView) {
define([
'jquery', 'underscore', 'gettext', 'js/views/pages/base_page', 'js/views/utils/xblock_utils',
'js/views/course_outline', 'common/js/components/utils/view_utils', 'common/js/components/views/feedback_alert',
'common/js/components/views/feedback_notification', 'js/views/course_highlights_enable'],
function($, _, gettext, BasePage, XBlockViewUtils, CourseOutlineView, ViewUtils, AlertView, NoteView,
CourseHighlightsEnableView
) {
'use strict';
var expandedLocators, CourseOutlinePage;
CourseOutlinePage = BasePage.extend({
......@@ -65,6 +69,15 @@ define(['jquery', 'underscore', 'gettext', 'js/views/pages/base_page', 'js/views
this.expandedLocators.addAll(this.initialState.expanded_locators);
}
/* globals course */
if (this.model.get('highlights_enabled') && course.get('self_paced')) {
this.highlightsEnableView = new CourseHighlightsEnableView({
el: this.$('.status-highlights-enabled'),
model: this.model
});
this.highlightsEnableView.render();
}
this.outlineView = new CourseOutlineView({
el: this.$('.outline'),
model: this.model,
......
......@@ -184,11 +184,13 @@
margin-bottom: $baseline;
.status-release,
.status-pacing {
.status-pacing,
.status-highlights-enabled {
@extend %t-copy-base;
display: inline-block;
color: $color-copy-base;
margin-right: $baseline;
// STATE: hover
&:hover {
......@@ -198,10 +200,17 @@
}
}
.status-highlights-enabled {
margin-left: $baseline * 1.6;
}
.status-release-label,
.status-release-value,
.status-pacing-label,
.status-pacing-value,
.status-highlights-enabled-label,
.status-highlights-enabled-value,
.status-highlights-enabled-info,
.status-actions {
display: inline-block;
vertical-align: middle;
......@@ -209,13 +218,28 @@
}
.status-release-label,
.status-pacing-label {
.status-pacing-label,
.status-highlights-enabled-label {
margin-right: ($baseline/4);
}
.status-release-value,
.status-pacing-value {
.status-pacing-value,
.status-highlights-enabled-value {
@extend %t-strong;
font-size: smaller;
}
.status-highlights-enabled-info {
font-size: smaller;
margin-left: $baseline / 2;
}
.status-highlights-enabled-value.button {
@extend %btn-primary-blue;
@extend %sizing;
padding: 5px 8px;
margin-top: 2px;
}
.status-actions {
......
......@@ -26,7 +26,7 @@ from openedx.core.djangolib.markup import HTML, Text
<%block name="header_extras">
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css')}" />
% 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']:
% 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', 'highlights-enable-editor', 'course-highlights-enable']:
<script type="text/template" id="${template_name}-tpl">
<%static:include path="js/${template_name}.underscore" />
</script>
......@@ -152,7 +152,8 @@ from openedx.core.djangolib.markup import HTML, Text
<article class="content-primary" role="main">
<div class="course-status">
<div class="status-release">
<h2 class="status-release-label">${_("Course Start Date:")}</h2>
<h2 class="status-release-label">${_("Course Start Date")}</h2>
<br>
<p class="status-release-value">${course_release_date}</p>
<ul class="status-actions">
......@@ -166,14 +167,16 @@ from openedx.core.djangolib.markup import HTML, Text
</div>
% if SelfPacedConfiguration.current().enabled:
<div class="status-pacing">
<h2 class=status-pacing-label>${_("Course Pacing:")}</h2>
<h2 class=status-pacing-label>${_("Course Pacing")}</h2>
<br>
% if context_course.self_paced:
<p class="status-pacing-value">${_("Self-Paced")}</p>
% else:
<p class="status-pacing-value">${_("Instructor-Paced")}</p>
% endif
</div>
% endif
% endif
<div class="status-highlights-enabled"></div>
</div>
<div class="wrapper-dnd"
% if getattr(context_course, 'language'):
......
<div class="course-highlights-setting">
<h2 id="highlights-enabled-label" class="status-highlights-enabled-label">
<%- gettext('Weekly Highlight Emails') %>
</h2>
<br>
<% if (highlights_enabled_for_messaging) { %>
<span class="status-highlights-enabled-value text"><%- gettext('Enabled') %></span>
<% } else { %>
<button class="status-highlights-enabled-value button" aria-labelledby="highlights-enabled-label"><%- gettext('Enable Now') %></button>
<% } %>
<a class="status-highlights-enabled-info" href="<%- highlights_doc_url %>">Learn more</a>
</div>
......@@ -19,7 +19,10 @@
'read our {linkStart}documentation{linkEnd}.'
),
{
linkStart: edx.HtmlUtils.HTML('<a href="' + highlights_doc_url + '">'),
linkStart: edx.HtmlUtils.interpolateHtml(
edx.HtmlUtils.HTML('<a href={highlightsDocUrl}">'),
{highlightsDocUrl: highlights_doc_url}
),
linkEnd: edx.HtmlUtils.HTML('</a>')
}
) %>
......
<p>
<%- gettext(
'When you enable weekly course highlight messages, learners ' +
'automatically receive weekly email messages for each section that ' +
'has highlights. You cannot disable highlights after you start ' +
'sending them.'
) %>
</p>
<p>
<% // xss-lint: disable=underscore-not-escaped %>
<%= edx.HtmlUtils.interpolateHtml(
gettext(
'Are you sure you want to enable weekly course highlight messages? '
+ '{linkStart}Learn more.{linkEnd}'
),
{
linkStart: edx.HtmlUtils.interpolateHtml(
edx.HtmlUtils.HTML('<a href="{highlightsDocUrl}" target="_blank">'),
{highlightsDocUrl: xblockInfo.attributes.highlights_doc_url}
),
linkEnd: edx.HtmlUtils.HTML('</a>')
}
) %>
</p>
......@@ -37,6 +37,9 @@
<div class="wrapper-content wrapper">
<section class="content">
<article class="content-primary" role="main">
<div class=course-status"">
<div class="status-highlights-enabled"></div>
</div>
<div class="wrapper-dnd">
<article class="outline outline-course" data-locator="mock-course" data-course-key="slashes:MockCourse">
<div class="no-content add-xblock-component">
......
......@@ -5,6 +5,7 @@ Acceptance tests for studio related to the outline page.
import itertools
import json
from datetime import datetime, timedelta
from unittest import skip
from nose.plugins.attrib import attr
from pytz import UTC
......@@ -115,6 +116,7 @@ class CourseOutlineDragAndDropTest(CourseOutlineTest):
expected_ordering
)
@skip("Fails in Firefox 45 but passes in Chrome")
def test_drop_unit_in_collapsed_subsection(self):
"""
Drag vertical "1.1.2" from subsection "1.1" into collapsed subsection "1.2" which already
......
"""
Contains methods for accessing weekly course highlights. Weekly highlights is a
schedule experience built on the Schedules app.
"""
import logging
from courseware.module_render import get_module_for_descriptor
......@@ -24,26 +28,28 @@ def course_has_highlights(course_key):
return False
else:
course_has_highlights = any(
highlights_are_available = any(
section.highlights
for section in course.get_children()
if not section.hide_from_toc
)
if not course_has_highlights:
if not highlights_are_available:
log.error(
"Course team enabled highlights and provided no highlights."
)
return course_has_highlights
return highlights_are_available
def get_week_highlights(user, course_key, week_num):
"""
Get highlights (list of unicode strings) for a given week.
week_num starts at 1.
Raises CourseUpdateDoesNotExist if highlights do not exist for
the requested week_num.
Raises:
CourseUpdateDoesNotExist: if highlights do not exist for
the requested week_num.
"""
course_descriptor = _get_course_with_highlights(course_key)
course_module = _get_course_module(course_descriptor, user)
......@@ -57,6 +63,7 @@ def get_week_highlights(user, course_key, week_num):
def _get_course_with_highlights(course_key):
# pylint: disable=missing-docstring
if not COURSE_UPDATE_WAFFLE_FLAG.is_enabled(course_key):
raise CourseUpdateDoesNotExist(
"%s Course Update Messages waffle flag is disabled.",
......
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