def _test_visible_to_students(self, expected_visible_without_lock, name, start_date, publish=False): """ Helper method that checks that is_xblock_visible_to_students returns the correct value both with and without visible_to_staff_only set. """ no_staff_lock = self._create_xblock_with_start_date(name, start_date, publish, visible_to_staff_only=False) self.assertEqual(expected_visible_without_lock, utils.is_currently_visible_to_students(no_staff_lock)) # any xblock with visible_to_staff_only set to True should not be visible to students. staff_lock = self._create_xblock_with_start_date( name + "_locked", start_date, publish, visible_to_staff_only=True ) self.assertFalse(utils.is_currently_visible_to_students(staff_lock))
def _test_visible_to_students(self, expected_visible_without_lock, name, start_date, publish=False): """ Helper method that checks that is_xblock_visible_to_students returns the correct value both with and without visible_to_staff_only set. """ no_staff_lock = self._create_xblock_with_start_date(name, start_date, publish, visible_to_staff_only=False) self.assertEqual(expected_visible_without_lock, utils.is_currently_visible_to_students(no_staff_lock)) # any xblock with visible_to_staff_only set to True should not be visible to students. staff_lock = self._create_xblock_with_start_date( name + "_locked", start_date, publish, visible_to_staff_only=True ) self.assertFalse(utils.is_currently_visible_to_students(staff_lock))
def add_container_page_publishing_info(xblock, xblock_info): # pylint: disable=invalid-name """ Adds information about the xblock's publish state to the supplied xblock_info for the container page. """ def safe_get_username(user_id): """ Guard against bad user_ids, like the infamous "**replace_user**". Note that this will ignore our special known IDs (ModuleStoreEnum.UserID). We should consider adding special handling for those values. :param user_id: the user id to get the username of :return: username, or None if the user does not exist or user_id is None """ if user_id: try: return User.objects.get(id=user_id).username except: # pylint: disable=bare-except pass return None xblock_info["edited_by"] = safe_get_username(xblock.subtree_edited_by) xblock_info["published_by"] = safe_get_username(xblock.published_by) xblock_info["currently_visible_to_students"] = is_currently_visible_to_students(xblock) xblock_info["has_content_group_components"] = has_children_visible_to_specific_content_groups(xblock) if xblock_info["release_date"]: xblock_info["release_date_from"] = _get_release_date_from(xblock) if xblock_info["visibility_state"] == VisibilityState.staff_only: xblock_info["staff_lock_from"] = _get_staff_lock_from(xblock) else: xblock_info["staff_lock_from"] = None
def add_container_page_publishing_info(xblock, xblock_info): # pylint: disable=invalid-name """ Adds information about the xblock's publish state to the supplied xblock_info for the container page. """ def safe_get_username(user_id): """ Guard against bad user_ids, like the infamous "**replace_user**". Note that this will ignore our special known IDs (ModuleStoreEnum.UserID). We should consider adding special handling for those values. :param user_id: the user id to get the username of :return: username, or None if the user does not exist or user_id is None """ if user_id: try: return User.objects.get(id=user_id).username except: # pylint: disable=bare-except pass return None xblock_info["edited_by"] = safe_get_username(xblock.subtree_edited_by) xblock_info["published_by"] = safe_get_username(xblock.published_by) xblock_info["currently_visible_to_students"] = is_currently_visible_to_students(xblock) xblock_info["has_content_group_components"] = has_children_visible_to_specific_content_groups(xblock) if xblock_info["release_date"]: xblock_info["release_date_from"] = _get_release_date_from(xblock) if xblock_info["visibility_state"] == VisibilityState.staff_only: xblock_info["staff_lock_from"] = _get_staff_lock_from(xblock) else: xblock_info["staff_lock_from"] = None
def test_draft_released_xblock(self): """Verifies that a xblock with an unreleased draft and a released published version is visible""" vertical = self._create_xblock_with_start_date("draft_released", self.past, publish=True) # Create an unreleased draft version of the xblock vertical.start = self.future modulestore().update_item(vertical, self.dummy_user) self.assertTrue(utils.is_currently_visible_to_students(vertical))
def test_draft_released_xblock(self): """Verifies that a xblock with an unreleased draft and a released published version is visible""" vertical = self._create_xblock_with_start_date('draft_released', self.past, publish=True) # Create an unreleased draft version of the xblock vertical.start = self.future modulestore().update_item(vertical, self.dummy_user) self.assertTrue(utils.is_currently_visible_to_students(vertical))
def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=False, include_child_info=False, course_outline=False, include_children_predicate=NEVER, parent_xblock=None, graders=None): """ Creates the information needed for client-side XBlockInfo. If data or metadata are not specified, their information will not be added (regardless of whether or not the xblock actually has data or metadata). There are three optional boolean parameters: include_ancestor_info - if true, ancestor info is added to the response include_child_info - if true, direct child info is included in the response course_outline - if true, the xblock is being rendered on behalf of the course outline. There are certain expensive computations that do not need to be included in this case. In addition, an optional include_children_predicate argument can be provided to define whether or not a particular xblock should have its children included. """ def safe_get_username(user_id): """ Guard against bad user_ids, like the infamous "**replace_user**". Note that this will ignore our special known IDs (ModuleStoreEnum.UserID). We should consider adding special handling for those values. :param user_id: the user id to get the username of :return: username, or None if the user does not exist or user_id is None """ if user_id: try: return User.objects.get(id=user_id).username except: # pylint: disable=bare-except pass return None is_xblock_unit = is_unit(xblock, parent_xblock) has_changes = modulestore().has_changes(xblock) if graders is None: graders = CourseGradingModel.fetch(xblock.location.course_key).graders # Compute the child info first so it can be included in aggregate information for the parent should_visit_children = include_child_info and (course_outline and not is_xblock_unit or not course_outline) if should_visit_children and xblock.has_children: child_info = _create_xblock_child_info( xblock, course_outline, graders, include_children_predicate=include_children_predicate, ) else: child_info = None # Treat DEFAULT_START_DATE as a magic number that means the release date has not been set release_date = get_default_time_display(xblock.start) if xblock.start != DEFAULT_START_DATE else None if xblock.category != 'course': visibility_state = _compute_visibility_state(xblock, child_info, is_xblock_unit and has_changes) else: visibility_state = None published = modulestore().has_published_version(xblock) xblock_info = { "id": unicode(xblock.location), "display_name": xblock.display_name_with_default, "category": xblock.category, "edited_on": get_default_time_display(xblock.subtree_edited_on) if xblock.subtree_edited_on else None, "published": published, "published_on": get_default_time_display(xblock.published_date) if xblock.published_date else None, "studio_url": xblock_studio_url(xblock, parent_xblock), "released_to_students": datetime.now(UTC) > xblock.start, "release_date": release_date, "visibility_state": visibility_state, "has_explicit_staff_lock": xblock.fields['visible_to_staff_only'].is_set_on(xblock), "start": xblock.fields['start'].to_json(xblock.start), "graded": xblock.graded, "due_date": get_default_time_display(xblock.due), "due": xblock.fields['due'].to_json(xblock.due), "format": xblock.format, "course_graders": json.dumps([grader.get('type') for grader in graders]), "has_changes": has_changes, } if data is not None: xblock_info["data"] = data if metadata is not None: xblock_info["metadata"] = metadata if include_ancestor_info: xblock_info['ancestor_info'] = _create_xblock_ancestor_info(xblock, course_outline) if child_info: xblock_info['child_info'] = child_info if visibility_state == VisibilityState.staff_only: xblock_info["ancestor_has_staff_lock"] = ancestor_has_staff_lock(xblock, parent_xblock) else: xblock_info["ancestor_has_staff_lock"] = False # Currently, 'edited_by', 'published_by', and 'release_date_from' are only used by the # container page when rendering a unit. Since they are expensive to compute, only include them for units # that are not being rendered on the course outline. if is_xblock_unit and not course_outline: xblock_info["edited_by"] = safe_get_username(xblock.subtree_edited_by) xblock_info["published_by"] = safe_get_username(xblock.published_by) xblock_info["currently_visible_to_students"] = is_currently_visible_to_students(xblock) if release_date: xblock_info["release_date_from"] = _get_release_date_from(xblock) if visibility_state == VisibilityState.staff_only: xblock_info["staff_lock_from"] = _get_staff_lock_from(xblock) else: xblock_info["staff_lock_from"] = None if course_outline: if xblock_info["has_explicit_staff_lock"]: xblock_info["staff_only_message"] = True elif child_info and child_info["children"]: xblock_info["staff_only_message"] = all([child["staff_only_message"] for child in child_info["children"]]) else: xblock_info["staff_only_message"] = False return xblock_info
def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=False, include_child_info=False, course_outline=False, include_children_predicate=NEVER, parent_xblock=None, graders=None): """ Creates the information needed for client-side XBlockInfo. If data or metadata are not specified, their information will not be added (regardless of whether or not the xblock actually has data or metadata). There are three optional boolean parameters: include_ancestor_info - if true, ancestor info is added to the response include_child_info - if true, direct child info is included in the response course_outline - if true, the xblock is being rendered on behalf of the course outline. There are certain expensive computations that do not need to be included in this case. In addition, an optional include_children_predicate argument can be provided to define whether or not a particular xblock should have its children included. """ def safe_get_username(user_id): """ Guard against bad user_ids, like the infamous "**replace_user**". Note that this will ignore our special known IDs (ModuleStoreEnum.UserID). We should consider adding special handling for those values. :param user_id: the user id to get the username of :return: username, or None if the user does not exist or user_id is None """ if user_id: try: return User.objects.get(id=user_id).username except: # pylint: disable=bare-except pass return None is_xblock_unit = is_unit(xblock, parent_xblock) # this should not be calculated for Sections and Subsections on Unit page has_changes = modulestore().has_changes(xblock) if ( is_xblock_unit or course_outline) else None if graders is None: graders = CourseGradingModel.fetch(xblock.location.course_key).graders # Compute the child info first so it can be included in aggregate information for the parent should_visit_children = include_child_info and ( course_outline and not is_xblock_unit or not course_outline) if should_visit_children and xblock.has_children: child_info = _create_xblock_child_info( xblock, course_outline, graders, include_children_predicate=include_children_predicate, ) else: child_info = None # Treat DEFAULT_START_DATE as a magic number that means the release date has not been set release_date = get_default_time_display( xblock.start) if xblock.start != DEFAULT_START_DATE else None if xblock.category != 'course': visibility_state = _compute_visibility_state( xblock, child_info, is_xblock_unit and has_changes) else: visibility_state = None published = modulestore().has_published_version(xblock) xblock_info = { "id": unicode(xblock.location), "display_name": xblock.display_name_with_default, "category": xblock.category, "edited_on": get_default_time_display(xblock.subtree_edited_on) if xblock.subtree_edited_on else None, "published": published, "published_on": get_default_time_display(xblock.published_on) if xblock.published_on else None, "studio_url": xblock_studio_url(xblock, parent_xblock), "released_to_students": datetime.now(UTC) > xblock.start, "release_date": release_date, "visibility_state": visibility_state, "lti_enabled": xblock.fields['lti_enabled'].is_set_on(xblock), "lti_url": "", "lti_key": "", "lti_secret": "", "has_explicit_staff_lock": xblock.fields['visible_to_staff_only'].is_set_on(xblock), "start": xblock.fields['start'].to_json(xblock.start), "graded": xblock.graded, "due_date": get_default_time_display(xblock.due), "due": xblock.fields['due'].to_json(xblock.due), "format": xblock.format, "course_graders": json.dumps([grader.get('type') for grader in graders]), "has_changes": has_changes, } if settings.FEATURES.get('ENABLE_AS_LTI_TOOL_PROVIDER', False): store = modulestore() course = store.get_course(xblock.location.course_key) if course.lti_enabled: course_id = xblock.location.course_key usr = int(xblock.subtree_edited_by) existing_components = LTIComponent.objects.filter( course_id=course_id, module_id=xblock.location, user_id=usr) if xblock.fields['lti_enabled'].is_set_on(xblock): if len(existing_components) == 0: key = ''.join( random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(8)) secret = ''.join( random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(16)) lti_component = LTIComponent(user_id=usr, course_id=course_id, module_id=str( xblock.location), key=key, secret=secret) lti_component.save() else: key = existing_components[0].key secret = existing_components[0].secret xblock_info["lti_url"] = str(xblock.location) xblock_info["lti_key"] = key xblock_info["lti_secret"] = secret else: if len(existing_components) > 0: for existing_component in existing_components: existing_components.delete() else: xblock_info["lti_url"] = "disabled" xblock_info["lti_key"] = "disabled" xblock_info["lti_secret"] = "disabled" else: xblock_info["lti_url"] = "disabled" xblock_info["lti_key"] = "disabled" xblock_info["lti_secret"] = "disabled" if data is not None: xblock_info["data"] = data if metadata is not None: xblock_info["metadata"] = metadata if include_ancestor_info: xblock_info['ancestor_info'] = _create_xblock_ancestor_info( xblock, course_outline) if child_info: xblock_info['child_info'] = child_info if visibility_state == VisibilityState.staff_only: xblock_info["ancestor_has_staff_lock"] = ancestor_has_staff_lock( xblock, parent_xblock) else: xblock_info["ancestor_has_staff_lock"] = False # Currently, 'edited_by', 'published_by', and 'release_date_from' are only used by the # container page when rendering a unit. Since they are expensive to compute, only include them for units # that are not being rendered on the course outline. if is_xblock_unit and not course_outline: xblock_info["edited_by"] = safe_get_username(xblock.subtree_edited_by) xblock_info["published_by"] = safe_get_username(xblock.published_by) xblock_info[ "currently_visible_to_students"] = is_currently_visible_to_students( xblock) if release_date: xblock_info["release_date_from"] = _get_release_date_from(xblock) if visibility_state == VisibilityState.staff_only: xblock_info["staff_lock_from"] = _get_staff_lock_from(xblock) else: xblock_info["staff_lock_from"] = None if course_outline: if xblock_info["has_explicit_staff_lock"]: xblock_info["staff_only_message"] = True elif child_info and child_info["children"]: xblock_info["staff_only_message"] = all([ child["staff_only_message"] for child in child_info["children"] ]) else: xblock_info["staff_only_message"] = False return xblock_info
def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=False, include_child_info=False, course_outline=False, include_children_predicate=NEVER, parent_xblock=None, graders=None): """ Creates the information needed for client-side XBlockInfo. If data or metadata are not specified, their information will not be added (regardless of whether or not the xblock actually has data or metadata). There are three optional boolean parameters: include_ancestor_info - if true, ancestor info is added to the response include_child_info - if true, direct child info is included in the response course_outline - if true, the xblock is being rendered on behalf of the course outline. There are certain expensive computations that do not need to be included in this case. In addition, an optional include_children_predicate argument can be provided to define whether or not a particular xblock should have its children included. """ def safe_get_username(user_id): """ Guard against bad user_ids, like the infamous "**replace_user**". Note that this will ignore our special known IDs (ModuleStoreEnum.UserID). We should consider adding special handling for those values. :param user_id: the user id to get the username of :return: username, or None if the user does not exist or user_id is None """ if user_id: try: return User.objects.get(id=user_id).username except: # pylint: disable=bare-except pass return None is_xblock_unit = is_unit(xblock, parent_xblock) is_unit_with_changes = is_xblock_unit and modulestore().has_changes(xblock) if graders is None: graders = CourseGradingModel.fetch(xblock.location.course_key).graders # Compute the child info first so it can be included in aggregate information for the parent should_visit_children = include_child_info and ( course_outline and not is_xblock_unit or not course_outline) if should_visit_children and xblock.has_children: child_info = _create_xblock_child_info( xblock, course_outline, graders, include_children_predicate=include_children_predicate, ) else: child_info = None # Treat DEFAULT_START_DATE as a magic number that means the release date has not been set release_date = get_default_time_display( xblock.start) if xblock.start != DEFAULT_START_DATE else None published = modulestore().compute_publish_state( xblock) != PublishState.private xblock_info = { "id": unicode(xblock.location), "display_name": xblock.display_name_with_default, "category": xblock.category, "edited_on": get_default_time_display(xblock.subtree_edited_on) if xblock.subtree_edited_on else None, "published": published, "published_on": get_default_time_display(xblock.published_date) if xblock.published_date else None, 'studio_url': xblock_studio_url(xblock, parent_xblock), "released_to_students": datetime.now(UTC) > xblock.start, "release_date": release_date, "visibility_state": _compute_visibility_state(xblock, child_info, is_unit_with_changes) if not xblock.category == 'course' else None, "start": xblock.fields['start'].to_json(xblock.start), "graded": xblock.graded, "due_date": get_default_time_display(xblock.due), "due": xblock.fields['due'].to_json(xblock.due), "format": xblock.format, "course_graders": json.dumps([grader.get('type') for grader in graders]), } if data is not None: xblock_info["data"] = data if metadata is not None: xblock_info["metadata"] = metadata if include_ancestor_info: xblock_info['ancestor_info'] = _create_xblock_ancestor_info( xblock, course_outline) if child_info: xblock_info['child_info'] = child_info # Currently, 'edited_by', 'published_by', and 'release_date_from', and 'has_changes' are only used by the # container page when rendering a unit. Since they are expensive to compute, only include them for units # that are not being rendered on the course outline. if is_xblock_unit and not course_outline: xblock_info["edited_by"] = safe_get_username(xblock.subtree_edited_by) xblock_info["published_by"] = safe_get_username(xblock.published_by) xblock_info[ "currently_visible_to_students"] = is_currently_visible_to_students( xblock) xblock_info['has_changes'] = is_unit_with_changes if release_date: xblock_info["release_date_from"] = _get_release_date_from(xblock) return xblock_info