diff --git a/cms/djangoapps/contentstore/course_group_config.py b/cms/djangoapps/contentstore/course_group_config.py
index aaae31bf42..6bcbee47a7 100644
--- a/cms/djangoapps/contentstore/course_group_config.py
+++ b/cms/djangoapps/contentstore/course_group_config.py
@@ -8,15 +8,16 @@ from util.db import generate_int_id, MYSQL_MAX_INT
from django.utils.translation import ugettext as _
from contentstore.utils import reverse_usage_url
+from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_user_partition
from xmodule.partitions.partitions import UserPartition, MINIMUM_STATIC_PARTITION_ID
from xmodule.partitions.partitions_service import get_all_partitions_for_course
from xmodule.split_test_module import get_split_user_partitions
-from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_user_partition
MINIMUM_GROUP_ID = MINIMUM_STATIC_PARTITION_ID
RANDOM_SCHEME = "random"
COHORT_SCHEME = "cohort"
+ENROLLMENT_SCHEME = "enrollment_track"
CONTENT_GROUP_CONFIGURATION_DESCRIPTION = _(
'The groups in this configuration can be mapped to cohorts in the Instructor Dashboard.'
@@ -187,21 +188,10 @@ class GroupConfiguration(object):
return usage_info
@staticmethod
- def get_content_groups_usage_info(store, course):
- """
- Get usage information for content groups.
- """
- items = store.get_items(course.id, settings={'group_access': {'$exists': True}}, include_orphans=False)
-
- return GroupConfiguration._get_content_groups_usage_info(course, items)
-
- @staticmethod
- def _get_content_groups_usage_info(course, items):
+ def get_partitions_usage_info(store, course):
"""
Returns all units names and their urls.
- This will return only groups for the cohort user partition.
-
Returns:
{'group_id':
[
@@ -216,8 +206,10 @@ class GroupConfiguration(object):
],
}
"""
+ items = store.get_items(course.id, settings={'group_access': {'$exists': True}}, include_orphans=False)
+
usage_info = {}
- for item, group_id in GroupConfiguration._iterate_items_and_content_group_ids(course, items):
+ for item, group_id in GroupConfiguration._iterate_items_and_group_ids(course, items):
if group_id not in usage_info:
usage_info[group_id] = []
@@ -267,7 +259,7 @@ class GroupConfiguration(object):
}
"""
usage_info = {}
- for item, group_id in GroupConfiguration._iterate_items_and_content_group_ids(course, items):
+ for item, group_id in GroupConfiguration._iterate_items_and_group_ids(course, items):
if group_id not in usage_info:
usage_info[group_id] = []
@@ -282,22 +274,23 @@ class GroupConfiguration(object):
return usage_info
@staticmethod
- def _iterate_items_and_content_group_ids(course, items):
+ def _iterate_items_and_group_ids(course, items):
"""
- Iterate through items and content group IDs in a course.
+ Iterate through items and group IDs in a course.
- This will yield group IDs *only* for cohort user partitions.
+ This will yield group IDs for all user partitions except those with a scheme of random.
Yields: tuple of (item, group_id)
"""
- content_group_configuration = get_cohorted_user_partition(course)
- if content_group_configuration is not None:
- for item in items:
- if hasattr(item, 'group_access') and item.group_access:
- group_ids = item.group_access.get(content_group_configuration.id, [])
+ all_partitions = get_all_partitions_for_course(course)
+ for config in all_partitions:
+ if config is not None and config.scheme.name != RANDOM_SCHEME:
+ for item in items:
+ if hasattr(item, 'group_access') and item.group_access:
+ group_ids = item.group_access.get(config.id, [])
- for group_id in group_ids:
- yield item, group_id
+ for group_id in group_ids:
+ yield item, group_id
@staticmethod
def update_usage_info(store, course, configuration):
@@ -319,23 +312,23 @@ class GroupConfiguration(object):
configuration_json['usage'] = usage_information.get(configuration.id, [])
elif configuration.scheme.name == COHORT_SCHEME:
# In case if scheme is "cohort"
- configuration_json = GroupConfiguration.update_content_group_usage_info(store, course, configuration)
+ configuration_json = GroupConfiguration.update_partition_usage_info(store, course, configuration)
return configuration_json
@staticmethod
- def update_content_group_usage_info(store, course, configuration):
+ def update_partition_usage_info(store, course, configuration):
"""
- Update usage information for particular Content Group Configuration.
+ Update usage information for particular Partition Configuration.
- Returns json of particular content group configuration updated with usage information.
+ Returns json of particular partition configuration updated with usage information.
"""
- usage_info = GroupConfiguration.get_content_groups_usage_info(store, course)
- content_group_configuration = configuration.to_json()
+ usage_info = GroupConfiguration.get_partitions_usage_info(store, course)
+ partition_configuration = configuration.to_json()
- for group in content_group_configuration['groups']:
+ for group in partition_configuration['groups']:
group['usage'] = usage_info.get(group['id'], [])
- return content_group_configuration
+ return partition_configuration
@staticmethod
def get_or_create_content_group(store, course):
@@ -357,9 +350,27 @@ class GroupConfiguration(object):
)
return content_group_configuration.to_json()
- content_group_configuration = GroupConfiguration.update_content_group_usage_info(
+ content_group_configuration = GroupConfiguration.update_partition_usage_info(
store,
course,
content_group_configuration
)
return content_group_configuration
+
+ @staticmethod
+ def get_all_user_partition_details(store, course):
+ """
+ Returns all the available partitions with updated usage information
+
+ :return: list of all partitions available with details
+ """
+ all_partitions = get_all_partitions_for_course(course)
+ all_updated_partitions = []
+ for partition in all_partitions:
+ configuration = GroupConfiguration.update_partition_usage_info(
+ store,
+ course,
+ partition
+ )
+ all_updated_partitions.append(configuration)
+ return all_updated_partitions
diff --git a/cms/djangoapps/contentstore/courseware_index.py b/cms/djangoapps/contentstore/courseware_index.py
index 99e0052e2e..ea4b819283 100644
--- a/cms/djangoapps/contentstore/courseware_index.py
+++ b/cms/djangoapps/contentstore/courseware_index.py
@@ -377,7 +377,7 @@ class CoursewareSearchIndexer(SearchIndexerBase):
@classmethod
def fetch_group_usage(cls, modulestore, structure):
groups_usage_dict = {}
- groups_usage_info = GroupConfiguration.get_content_groups_usage_info(modulestore, structure).items()
+ groups_usage_info = GroupConfiguration.get_partitions_usage_info(modulestore, structure).items()
groups_usage_info.extend(
GroupConfiguration.get_content_groups_items_usage_info(
modulestore,
diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py
index 4b883b0176..e3a5e45cea 100644
--- a/cms/djangoapps/contentstore/views/course.py
+++ b/cms/djangoapps/contentstore/views/course.py
@@ -30,6 +30,7 @@ from .library import LIBRARIES_ENABLED, get_library_creator_status
from ccx_keys.locator import CCXLocator
from contentstore.course_group_config import (
COHORT_SCHEME,
+ ENROLLMENT_SCHEME,
GroupConfiguration,
GroupConfigurationsValidationError,
RANDOM_SCHEME,
@@ -99,7 +100,6 @@ from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError, DuplicateCourseError
from xmodule.tabs import CourseTab, CourseTabList, InvalidTabsException
-
log = logging.getLogger(__name__)
__all__ = ['course_info_handler', 'course_handler', 'course_listing',
@@ -1473,7 +1473,7 @@ def remove_content_or_experiment_group(request, store, course, configuration, gr
return JsonResponse(status=404)
group_id = int(group_id)
- usages = GroupConfiguration.get_content_groups_usage_info(store, course)
+ usages = GroupConfiguration.get_partitions_usage_info(store, course)
used = group_id in usages
if used:
@@ -1521,7 +1521,24 @@ def group_configurations_list_handler(request, course_key_string):
else:
experiment_group_configurations = None
- content_group_configuration = GroupConfiguration.get_or_create_content_group(store, course)
+ all_partitions = GroupConfiguration.get_all_user_partition_details(store, course)
+ should_show_enrollment_track = False
+ group_schemes = []
+ for partition in all_partitions:
+ group_schemes.append(partition['scheme'])
+ if partition['scheme'] == ENROLLMENT_SCHEME:
+ enrollment_track_configuration = partition
+ should_show_enrollment_track = len(enrollment_track_configuration['groups']) > 1
+
+ # Remove the enrollment track partition and add it to the front of the list if it should be shown.
+ all_partitions.remove(partition)
+ if should_show_enrollment_track:
+ all_partitions.insert(0, partition)
+
+ # Add empty content group if there is no COHORT User Partition in the list.
+ # This will add ability to add new groups in the view.
+ if COHORT_SCHEME not in group_schemes:
+ all_partitions.append(GroupConfiguration.get_or_create_content_group(store, course))
return render_to_response('group_configurations.html', {
'context_course': course,
@@ -1529,7 +1546,8 @@ def group_configurations_list_handler(request, course_key_string):
'course_outline_url': course_outline_url,
'experiment_group_configurations': experiment_group_configurations,
'should_show_experiment_groups': should_show_experiment_groups,
- 'content_group_configuration': content_group_configuration
+ 'all_group_configurations': all_partitions,
+ 'should_show_enrollment_track': should_show_enrollment_track
})
elif "application/json" in request.META.get('HTTP_ACCEPT'):
if request.method == 'POST':
diff --git a/cms/djangoapps/contentstore/views/tests/test_group_configurations.py b/cms/djangoapps/contentstore/views/tests/test_group_configurations.py
index 8aafc88a8e..45f82f5c83 100644
--- a/cms/djangoapps/contentstore/views/tests/test_group_configurations.py
+++ b/cms/djangoapps/contentstore/views/tests/test_group_configurations.py
@@ -613,6 +613,15 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
def setUp(self):
super(GroupConfigurationsUsageInfoTestCase, self).setUp()
+ def _get_user_partition(self, scheme):
+ """
+ Returns the first user partition with the specified scheme.
+ """
+ for group in GroupConfiguration.get_all_user_partition_details(self.store, self.course):
+ if group['scheme'] == scheme:
+ return group
+ return None
+
def _get_expected_content_group(self, usage_for_group):
"""
Returns the expected configuration with particular usage.
@@ -637,7 +646,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
Test that right data structure will be created if content group is not used.
"""
self._add_user_partitions(scheme_id='cohort')
- actual = GroupConfiguration.get_or_create_content_group(self.store, self.course)
+ actual = self._get_user_partition('cohort')
expected = self._get_expected_content_group(usage_for_group=[])
self.assertEqual(actual, expected)
@@ -650,7 +659,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
cid=0, group_id=1, name_suffix='0', special_characters=u"JOSÉ ANDRÉS"
)
- actual = GroupConfiguration.get_or_create_content_group(self.store, self.course)
+ actual = self._get_user_partition('cohort')
expected = self._get_expected_content_group(
usage_for_group=[
{
@@ -669,7 +678,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
self._add_user_partitions(count=1, scheme_id='cohort')
vertical, __ = self._create_problem_with_content_group(cid=0, group_id=1, name_suffix='0')
- actual = GroupConfiguration.get_or_create_content_group(self.store, self.course)
+ actual = self._get_user_partition('cohort')
expected = self._get_expected_content_group(usage_for_group=[
{
@@ -706,7 +715,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
expected = self._get_expected_content_group(usage_for_group=[])
# Get the actual content group information
- actual = GroupConfiguration.get_or_create_content_group(self.store, self.course)
+ actual = self._get_user_partition('cohort')
# Assert that actual content group information is same as expected one.
self.assertEqual(actual, expected)
@@ -720,7 +729,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
vertical, __ = self._create_problem_with_content_group(cid=0, group_id=1, name_suffix='0')
vertical1, __ = self._create_problem_with_content_group(cid=0, group_id=1, name_suffix='1')
- actual = GroupConfiguration.get_or_create_content_group(self.store, self.course)
+ actual = self._get_user_partition('cohort')
expected = self._get_expected_content_group(usage_for_group=[
{
@@ -927,7 +936,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
# This used to cause an exception since the code assumed that
# only one partition would be available.
- actual = GroupConfiguration.get_content_groups_usage_info(self.store, self.course)
+ actual = GroupConfiguration.get_partitions_usage_info(self.store, self.course)
self.assertEqual(actual.keys(), [0])
actual = GroupConfiguration.get_content_groups_items_usage_info(self.store, self.course)
diff --git a/cms/djangoapps/contentstore/views/tests/test_item.py b/cms/djangoapps/contentstore/views/tests/test_item.py
index e53e8937e4..43df60059e 100644
--- a/cms/djangoapps/contentstore/views/tests/test_item.py
+++ b/cms/djangoapps/contentstore/views/tests/test_item.py
@@ -372,7 +372,7 @@ class GetItemTest(ItemTest):
self.assertEqual(result["user_partitions"], [
{
"id": ENROLLMENT_TRACK_PARTITION_ID,
- "name": "Enrollment Tracks",
+ "name": "Enrollment Track Groups",
"scheme": "enrollment_track",
"groups": [
{
diff --git a/cms/lib/xblock/test/test_authoring_mixin.py b/cms/lib/xblock/test/test_authoring_mixin.py
index b16eae9092..c45270c70a 100644
--- a/cms/lib/xblock/test/test_authoring_mixin.py
+++ b/cms/lib/xblock/test/test_authoring_mixin.py
@@ -22,7 +22,7 @@ class AuthoringMixinTestCase(ModuleStoreTestCase):
NO_CONTENT_ENROLLMENT_TRACK_ENABLED = "specific groups of learners based either on their enrollment track, or by content groups that you create"
NO_CONTENT_ENROLLMENT_TRACK_DISABLED = "specific groups of learners based on content groups that you create"
CONTENT_GROUPS_TITLE = "Content Groups"
- ENROLLMENT_GROUPS_TITLE = "Enrollment Tracks"
+ ENROLLMENT_GROUPS_TITLE = "Enrollment Track Groups"
STAFF_LOCKED = 'The unit that contains this component is hidden from learners'
FEATURES_WITH_ENROLLMENT_TRACK_DISABLED = settings.FEATURES.copy()
diff --git a/cms/static/js/factories/group_configurations.js b/cms/static/js/factories/group_configurations.js
index 32ab12172a..424f60a50c 100644
--- a/cms/static/js/factories/group_configurations.js
+++ b/cms/static/js/factories/group_configurations.js
@@ -2,24 +2,33 @@ define([
'js/collections/group_configuration', 'js/models/group_configuration', 'js/views/pages/group_configurations'
], function(GroupConfigurationCollection, GroupConfigurationModel, GroupConfigurationsPage) {
'use strict';
- return function(experimentsEnabled, experimentGroupConfigurationsJson, contentGroupConfigurationJson,
- groupConfigurationUrl, courseOutlineUrl) {
+ return function(experimentsEnabled,
+ experimentGroupConfigurationsJson,
+ allGroupConfigurationJson,
+ groupConfigurationUrl,
+ courseOutlineUrl) {
var experimentGroupConfigurations = new GroupConfigurationCollection(
experimentGroupConfigurationsJson, {parse: true}
),
- contentGroupConfiguration = new GroupConfigurationModel(contentGroupConfigurationJson, {
- parse: true, canBeEmpty: true
- });
+ allGroupConfigurations = [],
+ newGroupConfig,
+ i;
+
+ for (i = 0; i < allGroupConfigurationJson.length; i++) {
+ newGroupConfig = new GroupConfigurationModel(allGroupConfigurationJson[i],
+ {parse: true, canBeEmpty: true});
+ newGroupConfig.urlRoot = groupConfigurationUrl;
+ newGroupConfig.outlineUrl = courseOutlineUrl;
+ allGroupConfigurations.push(newGroupConfig);
+ }
experimentGroupConfigurations.url = groupConfigurationUrl;
experimentGroupConfigurations.outlineUrl = courseOutlineUrl;
- contentGroupConfiguration.urlRoot = groupConfigurationUrl;
- contentGroupConfiguration.outlineUrl = courseOutlineUrl;
new GroupConfigurationsPage({
el: $('#content'),
experimentsEnabled: experimentsEnabled,
experimentGroupConfigurations: experimentGroupConfigurations,
- contentGroupConfiguration: contentGroupConfiguration
+ allGroupConfigurations: allGroupConfigurations
}).render();
};
});
diff --git a/cms/static/js/spec/views/group_configuration_spec.js b/cms/static/js/spec/views/group_configuration_spec.js
index 3e6bc8d390..f90934b2b6 100644
--- a/cms/static/js/spec/views/group_configuration_spec.js
+++ b/cms/static/js/spec/views/group_configuration_spec.js
@@ -3,13 +3,13 @@ define([
'common/js/spec_helpers/view_helpers', 'js/models/course', 'js/models/group_configuration', 'js/models/group',
'js/collections/group_configuration', 'js/collections/group', 'js/views/group_configuration_details',
'js/views/group_configurations_list', 'js/views/group_configuration_editor', 'js/views/group_configuration_item',
- 'js/views/experiment_group_edit', 'js/views/content_group_list', 'js/views/content_group_details',
- 'js/views/content_group_editor', 'js/views/content_group_item'
+ 'js/views/experiment_group_edit', 'js/views/partition_group_list', 'js/views/partition_group_details',
+ 'js/views/content_group_editor', 'js/views/partition_group_item'
], function(
_, AjaxHelpers, TemplateHelpers, ViewHelpers, Course, GroupConfigurationModel, GroupModel,
GroupConfigurationCollection, GroupCollection, GroupConfigurationDetailsView, GroupConfigurationsListView,
GroupConfigurationEditorView, GroupConfigurationItemView, ExperimentGroupEditView, GroupList,
- ContentGroupDetailsView, ContentGroupEditorView, ContentGroupItemView
+ PartitionGroupDetailsView, ContentGroupEditorView, PartitionGroupItemView
) {
'use strict';
var SELECTORS = {
@@ -675,7 +675,7 @@ define([
verifyEditingGroup, respondToSave, expectGroupsVisible, correctValidationError;
scopedGroupSelector = function(groupIndex, additionalSelectors) {
- var groupSelector = '.content-groups-list-item-' + groupIndex;
+ var groupSelector = '.partition-groups-list-item-' + groupIndex;
if (additionalSelectors) {
return groupSelector + ' ' + additionalSelectors;
} else {
@@ -775,13 +775,13 @@ define([
expectGroupsVisible = function(view, groupNames) {
_.each(groupNames, function(groupName) {
- expect(view.$('.content-groups-list-item')).toContainText(groupName);
+ expect(view.$('.partition-groups-list-item')).toContainText(groupName);
});
};
beforeEach(function() {
TemplateHelpers.installTemplates(
- ['content-group-editor', 'content-group-details', 'list']
+ ['content-group-editor', 'partition-group-details', 'list']
);
});
@@ -792,7 +792,7 @@ define([
it('can render groups', function() {
var groupNames = ['Group 1', 'Group 2', 'Group 3'];
- renderView(groupNames).$('.content-group-details').each(function(index) {
+ renderView(groupNames).$('.partition-group-details').each(function(index) {
expect($(this)).toContainText(groupNames[index]);
});
});
@@ -874,7 +874,7 @@ define([
describe('Content groups details view', function() {
beforeEach(function() {
- TemplateHelpers.installTemplate('content-group-details', true);
+ TemplateHelpers.installTemplate('partition-group-details', true);
this.model = new GroupModel({name: 'Content Group', id: 0, courseOutlineUrl: 'CourseOutlineUrl'});
var saveableModel = new GroupConfigurationModel({
@@ -889,7 +889,7 @@ define([
this.collection = new GroupConfigurationCollection([saveableModel]);
this.collection.outlineUrl = '/outline';
- this.view = new ContentGroupDetailsView({
+ this.view = new PartitionGroupDetailsView({
model: this.model
});
appendSetFixtures(this.view.render().el);
@@ -901,7 +901,7 @@ define([
it('should show empty usage appropriately', function() {
this.view.$('.show-groups').click();
- assertShowEmptyUsages(this.view, 'This content group is not in use. ');
+ assertShowEmptyUsages(this.view, "Use this group to control a component's visibility in the ");
});
it('should hide empty usage appropriately', function() {
@@ -915,7 +915,7 @@ define([
assertShowNonEmptyUsages(
this.view,
- 'This content group is used in:',
+ 'This group controls visibility of:',
'Cannot delete when in use by a unit'
);
});
@@ -1015,7 +1015,7 @@ define([
describe('Content group controller view', function() {
beforeEach(function() {
TemplateHelpers.installTemplates([
- 'content-group-editor', 'content-group-details'
+ 'content-group-editor', 'partition-group-details'
], true);
this.model = new GroupModel({name: 'Content Group', id: 0, courseOutlineUrl: 'CourseOutlineUrl'});
@@ -1029,14 +1029,14 @@ define([
this.saveableModel.urlRoot = '/group_configurations';
this.collection = new GroupConfigurationCollection([this.saveableModel]);
this.collection.url = '/group_configurations';
- this.view = new ContentGroupItemView({
+ this.view = new PartitionGroupItemView({
model: this.model
});
appendSetFixtures(this.view.render().el);
});
it('should render properly', function() {
- assertControllerView(this.view, '.content-group-details', '.content-group-edit');
+ assertControllerView(this.view, '.partition-group-details', '.content-group-edit');
});
it('should destroy itself on confirmation of deleting', function() {
@@ -1047,7 +1047,7 @@ define([
assertAndDeleteItemWithError(
this,
'/group_configurations/0/0',
- '.content-groups-list-item',
+ '.partition-groups-list-item',
'Delete this content group'
);
});
diff --git a/cms/static/js/spec/views/pages/group_configurations_spec.js b/cms/static/js/spec/views/pages/group_configurations_spec.js
index 187c648c97..204b25c4fb 100644
--- a/cms/static/js/spec/views/pages/group_configurations_spec.js
+++ b/cms/static/js/spec/views/pages/group_configurations_spec.js
@@ -19,9 +19,8 @@ define([
name: 'Configuration 1',
courseOutlineUrl: 'CourseOutlineUrl'
}),
- contentGroupConfiguration: new GroupConfigurationModel({groups: []})
+ allGroupConfigurations: [new GroupConfigurationModel({groups: []})]
});
-
if (!disableSpy) {
spyOn(view, 'addWindowActions');
}
@@ -36,7 +35,7 @@ define([
beforeEach(function() {
setFixtures(mockGroupConfigurationsPage);
TemplateHelpers.installTemplates([
- 'group-configuration-editor', 'group-configuration-details', 'content-group-details',
+ 'group-configuration-editor', 'group-configuration-details', 'partition-group-details',
'content-group-editor', 'group-edit', 'list'
]);
@@ -116,7 +115,7 @@ define([
});
it('should show a notification message if a content group is changed', function() {
- this.view.contentGroupConfiguration.get('groups').add({id: 0, name: 'Content Group'});
+ this.view.allGroupConfigurations[0].get('groups').add({id: 0, name: 'Content Group'});
expect(this.view.onBeforeUnload())
.toBe('You have unsaved changes. Do you really want to leave this page?');
});
diff --git a/cms/static/js/views/content_group_list.js b/cms/static/js/views/content_group_list.js
deleted file mode 100644
index 8f5e420a96..0000000000
--- a/cms/static/js/views/content_group_list.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * This class defines a list view for content groups.
- * It is expected to be backed by a Group collection.
- */
-define([
- 'js/views/list', 'js/views/content_group_item', 'gettext'
-], function(ListView, ContentGroupItemView, gettext) {
- 'use strict';
-
- var ContentGroupListView = ListView.extend({
- tagName: 'div',
-
- className: 'content-group-list',
-
- // Translators: This refers to a content group that can be linked to a student cohort.
- itemCategoryDisplayName: gettext('content group'),
-
- newItemMessage: gettext('Add your first content group'),
-
- emptyMessage: gettext('You have not created any content groups yet.'),
-
- createItemView: function(options) {
- return new ContentGroupItemView(options);
- }
- });
-
- return ContentGroupListView;
-});
diff --git a/cms/static/js/views/list.js b/cms/static/js/views/list.js
index a0cd130a0b..7ec03927f2 100644
--- a/cms/static/js/views/list.js
+++ b/cms/static/js/views/list.js
@@ -11,6 +11,7 @@
* of items this list contains. For example, 'Group Configuration'.
* Note that it must be translated.
* - emptyMessage (string): Text to render when the list is empty.
+ * - restrictEditing (bool) : Boolean flag for hiding edit and remove options, defaults to false.
*/
define([
'js/views/baseview'
@@ -25,6 +26,7 @@ define([
listContainerCss: '.list-items',
initialize: function() {
+ this.restrictEditing = this.options.restrictEditing || false;
this.listenTo(this.collection, 'add', this.addNewItemView);
this.listenTo(this.collection, 'remove', this.onRemoveItem);
this.template = this.loadTemplate('list');
@@ -42,11 +44,14 @@ define([
emptyMessage: this.emptyMessage,
length: this.collection.length,
isEditing: model && model.get('editing'),
- canCreateNewItem: this.canCreateItem(this.collection)
+ canCreateNewItem: this.canCreateItem(this.collection),
+ restrictEditing: this.restrictEditing
}));
this.collection.each(function(model) {
- this.$(this.listContainerCss).append(this.createItemView({model: model}).render().el);
+ this.$(this.listContainerCss).append(
+ this.createItemView({model: model, restrictEditing: this.restrictEditing}).render().el
+ );
}, this);
return this;
diff --git a/cms/static/js/views/list_item.js b/cms/static/js/views/list_item.js
index 692fc2c54f..e51c9caede 100644
--- a/cms/static/js/views/list_item.js
+++ b/cms/static/js/views/list_item.js
@@ -22,6 +22,7 @@ define([
canDelete: false,
initialize: function() {
+ this.restrictEditing = this.options.restrictEditing || false;
this.listenTo(this.model, 'change:editing', this.render);
this.listenTo(this.model, 'remove', this.remove);
},
diff --git a/cms/static/js/views/pages/group_configurations.js b/cms/static/js/views/pages/group_configurations.js
index 69b0b779f1..9da77189bc 100644
--- a/cms/static/js/views/pages/group_configurations.js
+++ b/cms/static/js/views/pages/group_configurations.js
@@ -1,11 +1,15 @@
define([
'jquery', 'underscore', 'gettext', 'js/views/pages/base_page',
- 'js/views/group_configurations_list', 'js/views/content_group_list'
+ 'js/views/group_configurations_list', 'js/views/partition_group_list'
],
-function($, _, gettext, BasePage, GroupConfigurationsListView, ContentGroupListView) {
+function($, _, gettext, BasePage, GroupConfigurationsListView, PartitionGroupListView) {
'use strict';
var GroupConfigurationsPage = BasePage.extend({
initialize: function(options) {
+ var currentScheme,
+ i,
+ enrollmentScheme = 'enrollment_track';
+
BasePage.prototype.initialize.call(this);
this.experimentsEnabled = options.experimentsEnabled;
if (this.experimentsEnabled) {
@@ -14,18 +18,35 @@ function($, _, gettext, BasePage, GroupConfigurationsListView, ContentGroupListV
collection: this.experimentGroupConfigurations
});
}
- this.contentGroupConfiguration = options.contentGroupConfiguration;
- this.cohortGroupsListView = new ContentGroupListView({
- collection: this.contentGroupConfiguration.get('groups')
- });
+
+ this.allGroupConfigurations = options.allGroupConfigurations || [];
+ this.allGroupViewList = [];
+ for (i = 0; i < this.allGroupConfigurations.length; i++) {
+ currentScheme = this.allGroupConfigurations[i].get('scheme');
+ this.allGroupViewList.push(
+ new PartitionGroupListView({
+ collection: this.allGroupConfigurations[i].get('groups'),
+ restrictEditing: currentScheme === enrollmentScheme,
+ scheme: currentScheme
+ })
+ );
+ }
},
renderPage: function() {
- var hash = this.getLocationHash();
+ var hash = this.getLocationHash(),
+ i,
+ currentClass;
if (this.experimentsEnabled) {
this.$('.wrapper-groups.experiment-groups').append(this.experimentGroupsListView.render().el);
}
- this.$('.wrapper-groups.content-groups').append(this.cohortGroupsListView.render().el);
+
+ // Render the remaining Configuration groups
+ for (i = 0; i < this.allGroupViewList.length; i++) {
+ currentClass = '.wrapper-groups.content-groups.' + this.allGroupViewList[i].scheme;
+ this.$(currentClass).append(this.allGroupViewList[i].render().el);
+ }
+
this.addWindowActions();
if (hash) {
// Strip leading '#' to get id string to match
@@ -38,8 +59,22 @@ function($, _, gettext, BasePage, GroupConfigurationsListView, ContentGroupListV
$(window).on('beforeunload', this.onBeforeUnload.bind(this));
},
+ /**
+ * Checks the Partition Group Configurations to see if the isDirty bit is set
+ * @returns {boolean} True if any partition group has the dirty bit set.
+ */
+ areAnyConfigurationsDirty: function() {
+ var i;
+ for (i = 0; i < this.allGroupConfigurations.length; i++) {
+ if (this.allGroupConfigurations[i].isDirty()) {
+ return true;
+ }
+ }
+ return false;
+ },
+
onBeforeUnload: function() {
- var dirty = this.contentGroupConfiguration.isDirty() ||
+ var dirty = this.areAnyConfigurationsDirty() ||
(this.experimentsEnabled && this.experimentGroupConfigurations.find(function(configuration) {
return configuration.isDirty();
}));
diff --git a/cms/static/js/views/content_group_details.js b/cms/static/js/views/partition_group_details.js
similarity index 78%
rename from cms/static/js/views/content_group_details.js
rename to cms/static/js/views/partition_group_details.js
index 562b7073c0..bfd96808b7 100644
--- a/cms/static/js/views/content_group_details.js
+++ b/cms/static/js/views/partition_group_details.js
@@ -1,5 +1,5 @@
/**
- * This class defines a simple display view for a content group.
+ * This class defines a simple display view for a partition group.
* It is expected to be backed by a Group model.
*/
define([
@@ -8,7 +8,7 @@ define([
], function(BaseView, _, gettext, str, StringUtils, HtmlUtils) {
'use strict';
- var ContentGroupDetailsView = BaseView.extend({
+ var PartitionGroupDetailsView = BaseView.extend({
tagName: 'div',
events: {
'click .edit': 'editGroup',
@@ -21,17 +21,18 @@ define([
return [
'collection',
- 'content-group-details',
- 'content-group-details-' + index
+ 'partition-group-details',
+ 'partition-group-details-' + index
].join(' ');
},
editGroup: function() {
- this.model.set({'editing': true});
+ this.model.set({editing: true});
},
initialize: function() {
- this.template = this.loadTemplate('content-group-details');
+ this.template = this.loadTemplate('partition-group-details');
+ this.restrictEditing = this.options.restrictEditing || false;
this.listenTo(this.model, 'change', this.render);
},
@@ -41,9 +42,10 @@ define([
courseOutlineUrl: this.model.collection.parents[0].outlineUrl,
index: this.model.collection.indexOf(this.model),
showContentGroupUsages: showContentGroupUsages || false,
- HtmlUtils: HtmlUtils
+ HtmlUtils: HtmlUtils,
+ restrictEditing: this.restrictEditing
});
- this.$el.html(this.template(attrs));
+ HtmlUtils.setHtml(this.$el, HtmlUtils.HTML(this.template(attrs)));
return this;
},
@@ -78,5 +80,5 @@ define([
}
});
- return ContentGroupDetailsView;
+ return PartitionGroupDetailsView;
});
diff --git a/cms/static/js/views/content_group_item.js b/cms/static/js/views/partition_group_item.js
similarity index 53%
rename from cms/static/js/views/content_group_item.js
rename to cms/static/js/views/partition_group_item.js
index 0b647d3435..73d7668f92 100644
--- a/cms/static/js/views/content_group_item.js
+++ b/cms/static/js/views/partition_group_item.js
@@ -1,22 +1,23 @@
/**
- * This class defines an controller view for content groups.
+ * This class defines an controller view for partition groups.
* It renders an editor view or a details view depending on the state
* of the underlying model.
* It is expected to be backed by a Group model.
*/
define([
- 'js/views/list_item', 'js/views/content_group_editor', 'js/views/content_group_details', 'gettext', 'common/js/components/utils/view_utils'
-], function(ListItemView, ContentGroupEditorView, ContentGroupDetailsView, gettext) {
+ 'js/views/list_item', 'js/views/content_group_editor', 'js/views/partition_group_details',
+ 'gettext', 'common/js/components/utils/view_utils'
+], function(ListItemView, ContentGroupEditorView, PartitionGroupDetailsView, gettext) {
'use strict';
- var ContentGroupItemView = ListItemView.extend({
+ var PartitionGroupItemView = ListItemView.extend({
events: {
'click .delete': 'deleteItem'
},
tagName: 'section',
- baseClassName: 'content-group',
+ baseClassName: 'partition-group',
canDelete: true,
@@ -24,8 +25,8 @@ define([
attributes: function() {
return {
- 'id': this.model.get('id'),
- 'tabindex': -1
+ id: this.model.get('id'),
+ tabindex: -1
};
},
@@ -34,9 +35,11 @@ define([
},
createDetailsView: function() {
- return new ContentGroupDetailsView({model: this.model});
+ return new PartitionGroupDetailsView({model: this.model,
+ restrictEditing: this.options.restrictEditing
+ });
}
});
- return ContentGroupItemView;
+ return PartitionGroupItemView;
});
diff --git a/cms/static/js/views/partition_group_list.js b/cms/static/js/views/partition_group_list.js
new file mode 100644
index 0000000000..602889e1b7
--- /dev/null
+++ b/cms/static/js/views/partition_group_list.js
@@ -0,0 +1,33 @@
+/**
+ * This class defines a list view for partition groups.
+ * It is expected to be backed by a Group collection.
+ */
+define([
+ 'underscore', 'js/views/list', 'js/views/partition_group_item', 'gettext'
+], function(_, ListView, PartitionGroupItemView, gettext) {
+ 'use strict';
+
+ var PartitionGroupListView = ListView.extend({
+ initialize: function(options) {
+ ListView.prototype.initialize.apply(this, [options]);
+ this.scheme = options.scheme;
+ },
+
+ tagName: 'div',
+
+ className: 'partition-group-list',
+
+ // Translators: This refers to a content group that can be linked to a student cohort.
+ itemCategoryDisplayName: gettext('content group'),
+
+ newItemMessage: gettext('Add your first content group'),
+
+ emptyMessage: gettext('You have not created any content groups yet.'),
+
+ createItemView: function(options) {
+ return new PartitionGroupItemView(_.extend({}, options, {scheme: this.scheme}));
+ }
+ });
+
+ return PartitionGroupListView;
+});
diff --git a/cms/templates/group_configurations.html b/cms/templates/group_configurations.html
index e883d2fed9..c8f06d7207 100644
--- a/cms/templates/group_configurations.html
+++ b/cms/templates/group_configurations.html
@@ -1,6 +1,7 @@
<%page expression_filter="h"/>
<%inherit file="base.html" />
<%def name="content_groups_help_token()"><% return "content_groups" %>%def>
+<%def name="enrollment_track_help_token()"><% return "enrollment_tracks" %>%def>
<%def name="experiment_group_configurations_help_token()"><% return "group_configurations" %>%def>
<%namespace name='static' file='static_content.html'/>
<%!
@@ -16,7 +17,7 @@ from openedx.core.djangolib.markup import HTML, Text
<%block name="bodyclass">is-signedin course view-group-configurations%block>
<%block name="header_extras">
-% for template_name in ["group-configuration-details", "group-configuration-editor", "group-edit", "content-group-editor", "content-group-details", "basic-modal", "modal-button", "list"]:
+% for template_name in ["group-configuration-details", "group-configuration-editor", "group-edit", "content-group-editor", "partition-group-details", "basic-modal", "modal-button", "list"]:
@@ -28,9 +29,10 @@ from openedx.core.djangolib.markup import HTML, Text
GroupConfigurationsFactory(
${should_show_experiment_groups | n, dump_js_escaped_json},
${experiment_group_configurations | n, dump_js_escaped_json},
- ${content_group_configuration | n, dump_js_escaped_json},
+ ${all_group_configurations | n, dump_js_escaped_json},
"${group_configuration_url | n, js_escaped_string}",
- "${course_outline_url | n, js_escaped_string}"
+ "${course_outline_url | n, js_escaped_string}",
+ ${should_show_enrollment_track | n, dump_js_escaped_json}
);
});
%block>
@@ -47,37 +49,52 @@ from openedx.core.djangolib.markup import HTML, Text
-
-
-
${_("Content Groups")}
-
-
- % if should_show_experiment_groups:
-
-
${_("Experiment Group Configurations")}
- % if experiment_group_configurations is None:
-
-
- ${_("This module is disabled at the moment.")}
-
-
- % else:
-
- % endif
-
- % endif
+
+
+ % for config in all_group_configurations:
+
+ % endfor
+
+ % if should_show_experiment_groups:
+
+
${_("Experiment Group Configurations")}
+ % if experiment_group_configurations is None:
+
+
+ ${_("This module is disabled at the moment.")}
+
+
+ % else:
+
+ % endif
+
+ % endif