diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index 711b3947f8..03a2b6aae2 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -13,6 +13,8 @@ from django.utils.translation import ugettext as _ from django_comment_common.models import assign_default_role from django_comment_common.utils import seed_permissions_roles +from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration + from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import ItemNotFoundError @@ -455,3 +457,10 @@ def get_visibility_partition_info(xblock): "has_selected_groups": has_selected_groups, "selected_verified_partition_id": selected_verified_partition_id, } + + +def is_self_paced(course): + """ + Returns True if course is self-paced, False otherwise. + """ + return course and course.self_paced and SelfPacedConfiguration.current().enabled diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index 6b305b075f..ada012c6ec 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -31,6 +31,8 @@ from contentstore.utils import ( from contentstore.views.helpers import is_unit, xblock_studio_url, xblock_primary_child_category, \ xblock_type_display_name, get_parent_xblock, create_xblock, usage_key_with_run from contentstore.views.preview import get_preview_fragment +from contentstore.utils import is_self_paced + from openedx.core.lib.gating import api as gating_api from edxmako.shortcuts import render_to_string from models.settings.course_grading import CourseGradingModel @@ -855,7 +857,9 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F release_date = _get_release_date(xblock, user) if xblock.category != 'course': - visibility_state = _compute_visibility_state(xblock, child_info, is_xblock_unit and has_changes) + visibility_state = _compute_visibility_state( + xblock, child_info, is_xblock_unit and has_changes, is_self_paced(course) + ) else: visibility_state = None published = modulestore().has_published_version(xblock) if not is_library_block else None @@ -1017,7 +1021,7 @@ class VisibilityState(object): gated = 'gated' -def _compute_visibility_state(xblock, child_info, is_unit_with_changes): +def _compute_visibility_state(xblock, child_info, is_unit_with_changes, is_course_self_paced=False): """ Returns the current publish state for the specified xblock and its children """ @@ -1027,10 +1031,10 @@ def _compute_visibility_state(xblock, child_info, is_unit_with_changes): # Note that a unit that has never been published will fall into this category, # as well as previously published units with draft content. return VisibilityState.needs_attention + is_unscheduled = xblock.start == DEFAULT_START_DATE - is_live = datetime.now(UTC) > xblock.start - children = child_info and child_info.get('children', []) - if children and len(children) > 0: + is_live = is_course_self_paced or datetime.now(UTC) > xblock.start + if child_info and child_info.get('children', []): all_staff_only = True all_unscheduled = True all_live = True diff --git a/cms/djangoapps/contentstore/views/tests/test_item.py b/cms/djangoapps/contentstore/views/tests/test_item.py index 558bdeeae2..03024fb587 100644 --- a/cms/djangoapps/contentstore/views/tests/test_item.py +++ b/cms/djangoapps/contentstore/views/tests/test_item.py @@ -14,6 +14,7 @@ from django.test.client import RequestFactory from django.core.urlresolvers import reverse from contentstore.utils import reverse_usage_url, reverse_course_url +from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration from contentstore.views.component import ( component_handler, get_component_templates ) @@ -1847,6 +1848,7 @@ class TestLibraryXBlockCreation(ItemTest): self.assertFalse(lib.children) +@ddt.ddt class TestXBlockPublishingInfo(ItemTest): """ Unit tests for XBlock's outline handling. @@ -2169,3 +2171,31 @@ class TestXBlockPublishingInfo(ItemTest): self._verify_has_staff_only_message(xblock_info, True) self._verify_has_staff_only_message(xblock_info, True, path=self.FIRST_SUBSECTION_PATH) self._verify_has_staff_only_message(xblock_info, True, path=self.FIRST_UNIT_PATH) + + @ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split) + def test_self_paced_item_visibility_state(self, store_type): + """ + Test that in self-paced course, item has `live` visibility state. + Test that when item was initially in `scheduled` state in instructor mode, change course pacing to self-paced, + now in self-paced course, item should have `live` visibility state. + """ + SelfPacedConfiguration(enabled=True).save() + + # Create course, chapter and setup future release date to make chapter in scheduled state + course = CourseFactory.create(default_store=store_type) + chapter = self._create_child(course, 'chapter', "Test Chapter") + self._set_release_date(chapter.location, datetime.now(UTC) + timedelta(days=1)) + + # Check that chapter has scheduled state + xblock_info = self._get_xblock_info(chapter.location) + self._verify_visibility_state(xblock_info, VisibilityState.ready) + self.assertFalse(course.self_paced) + + # Change course pacing to self paced + course.self_paced = True + self.store.update_item(course, self.user.id) + self.assertTrue(course.self_paced) + + # Check that in self paced course content has live state now + xblock_info = self._get_xblock_info(chapter.location) + self._verify_visibility_state(xblock_info, VisibilityState.live)