Merge pull request #15289 from edx/jlajoie/EDUCATOR-434
EDUCATOR-434: Unit Group Access
This commit is contained in:
@@ -7,6 +7,7 @@ import logging
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from contentstore.utils import reverse_usage_url
|
||||
from lms.lib.utils import get_parent_unit
|
||||
from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_user_partition
|
||||
from util.db import MYSQL_MAX_INT, generate_int_id
|
||||
from xmodule.partitions.partitions import MINIMUM_STATIC_PARTITION_ID, UserPartition
|
||||
@@ -111,9 +112,30 @@ class GroupConfiguration(object):
|
||||
"""
|
||||
Get usage info for unit/module.
|
||||
"""
|
||||
parent_unit = get_parent_unit(item)
|
||||
|
||||
if unit == parent_unit and not item.has_children:
|
||||
# Display the topmost unit page if
|
||||
# the item is a child of the topmost unit and doesn't have its own children.
|
||||
unit_for_url = unit
|
||||
elif (not parent_unit and unit.get_parent()) or (unit == parent_unit and item.has_children):
|
||||
# Display the item's page rather than the unit page if
|
||||
# the item is one level below the topmost unit and has children, or
|
||||
# the item itself *is* the topmost unit (and thus does not have a parent unit, but is not an orphan).
|
||||
unit_for_url = item
|
||||
else:
|
||||
# If the item is nested deeper than two levels (the topmost unit > vertical > ... > item)
|
||||
# display the page for the nested vertical element.
|
||||
parent = item.get_parent()
|
||||
nested_vertical = item
|
||||
while parent != parent_unit:
|
||||
nested_vertical = parent
|
||||
parent = parent.get_parent()
|
||||
unit_for_url = nested_vertical
|
||||
|
||||
unit_url = reverse_usage_url(
|
||||
'container_handler',
|
||||
course.location.course_key.make_usage_key(unit.location.block_type, unit.location.name)
|
||||
course.location.course_key.make_usage_key(unit_for_url.location.block_type, unit_for_url.location.name)
|
||||
)
|
||||
|
||||
usage_dict = {'label': u"{} / {}".format(unit.display_name, item.display_name), 'url': unit_url}
|
||||
|
||||
@@ -431,7 +431,7 @@ def get_user_partition_info(xblock, schemes=None, course=None):
|
||||
return partitions
|
||||
|
||||
|
||||
def get_visibility_partition_info(xblock):
|
||||
def get_visibility_partition_info(xblock, course=None):
|
||||
"""
|
||||
Retrieve user partition information for the component visibility editor.
|
||||
|
||||
@@ -440,12 +440,16 @@ def get_visibility_partition_info(xblock):
|
||||
Arguments:
|
||||
xblock (XBlock): The component being edited.
|
||||
|
||||
course (XBlock): The course descriptor. If provided, uses this to look up the user partitions
|
||||
instead of loading the course. This is useful if we're calling this function multiple
|
||||
times for the same course want to minimize queries to the modulestore.
|
||||
|
||||
Returns: dict
|
||||
|
||||
"""
|
||||
selectable_partitions = []
|
||||
# We wish to display enrollment partitions before cohort partitions.
|
||||
enrollment_user_partitions = get_user_partition_info(xblock, schemes=["enrollment_track"])
|
||||
enrollment_user_partitions = get_user_partition_info(xblock, schemes=["enrollment_track"], course=course)
|
||||
|
||||
# For enrollment partitions, we only show them if there is a selected group or
|
||||
# or if the number of groups > 1.
|
||||
@@ -454,7 +458,7 @@ def get_visibility_partition_info(xblock):
|
||||
selectable_partitions.append(partition)
|
||||
|
||||
# Now add the cohort user partitions.
|
||||
selectable_partitions = selectable_partitions + get_user_partition_info(xblock, schemes=["cohort"])
|
||||
selectable_partitions = selectable_partitions + get_user_partition_info(xblock, schemes=["cohort"], course=course)
|
||||
|
||||
# Find the first partition with a selected group. That will be the one initially enabled in the dialog
|
||||
# (if the course has only been added in Studio, only one partition should have a selected group).
|
||||
|
||||
@@ -46,8 +46,8 @@ CONTAINER_TEMPLATES = [
|
||||
"editor-mode-button", "upload-dialog",
|
||||
"add-xblock-component", "add-xblock-component-button", "add-xblock-component-menu",
|
||||
"add-xblock-component-support-legend", "add-xblock-component-support-level", "add-xblock-component-menu-problem",
|
||||
"xblock-string-field-editor", "publish-xblock", "publish-history",
|
||||
"unit-outline", "container-message", "license-selector",
|
||||
"xblock-string-field-editor", "xblock-access-editor", "publish-xblock", "publish-history",
|
||||
"unit-outline", "container-message", "container-access", "license-selector",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ from contentstore.utils import (
|
||||
find_staff_lock_source,
|
||||
get_split_group_display_name,
|
||||
get_user_partition_info,
|
||||
get_visibility_partition_info,
|
||||
has_children_visible_to_specific_partition_groups,
|
||||
is_currently_visible_to_students,
|
||||
is_self_paced
|
||||
@@ -1231,9 +1232,11 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
|
||||
else:
|
||||
xblock_info['staff_only_message'] = False
|
||||
|
||||
xblock_info["has_partition_group_components"] = has_children_visible_to_specific_partition_groups(
|
||||
xblock_info['has_partition_group_components'] = has_children_visible_to_specific_partition_groups(
|
||||
xblock
|
||||
)
|
||||
xblock_info['user_partition_info'] = get_visibility_partition_info(xblock, course=course)
|
||||
|
||||
return xblock_info
|
||||
|
||||
|
||||
|
||||
@@ -38,15 +38,22 @@ class HelperMethods(object):
|
||||
"""
|
||||
Mixin that provides useful methods for Group Configuration tests.
|
||||
"""
|
||||
def _create_content_experiment(self, cid=-1, name_suffix='', special_characters=''):
|
||||
def _create_content_experiment(self, cid=-1, group_id=None, cid_for_problem=None,
|
||||
name_suffix='', special_characters=''):
|
||||
"""
|
||||
Create content experiment.
|
||||
|
||||
Assign Group Configuration to the experiment if cid is provided.
|
||||
Assigns a problem to the first group in the split test if group_id and cid_for_problem is provided.
|
||||
"""
|
||||
sequential = ItemFactory.create(
|
||||
category='sequential',
|
||||
parent_location=self.course.location,
|
||||
display_name='Test Subsection {}'.format(name_suffix)
|
||||
)
|
||||
vertical = ItemFactory.create(
|
||||
category='vertical',
|
||||
parent_location=self.course.location,
|
||||
parent_location=sequential.location,
|
||||
display_name='Test Unit {}'.format(name_suffix)
|
||||
)
|
||||
c0_url = self.course.id.make_usage_key("vertical", "split_test_cond0")
|
||||
@@ -65,7 +72,7 @@ class HelperMethods(object):
|
||||
display_name="Condition 0 vertical",
|
||||
location=c0_url,
|
||||
)
|
||||
ItemFactory.create(
|
||||
c1_vertical = ItemFactory.create(
|
||||
parent_location=split_test.location,
|
||||
category="vertical",
|
||||
display_name="Condition 1 vertical",
|
||||
@@ -78,6 +85,19 @@ class HelperMethods(object):
|
||||
location=c2_url,
|
||||
)
|
||||
|
||||
problem = None
|
||||
if group_id and cid_for_problem:
|
||||
problem = ItemFactory.create(
|
||||
category='problem',
|
||||
parent_location=c1_vertical.location,
|
||||
display_name=u"Test Problem"
|
||||
)
|
||||
self.client.ajax_post(
|
||||
reverse_usage_url("xblock_handler", problem.location),
|
||||
data={'metadata': {'group_access': {cid_for_problem: [group_id]}}}
|
||||
)
|
||||
c1_vertical.children.append(problem.location)
|
||||
|
||||
partitions_json = [p.to_json() for p in self.course.user_partitions]
|
||||
|
||||
self.client.ajax_post(
|
||||
@@ -86,16 +106,25 @@ class HelperMethods(object):
|
||||
)
|
||||
|
||||
self.save_course()
|
||||
return (vertical, split_test)
|
||||
return vertical, split_test, problem
|
||||
|
||||
def _create_problem_with_content_group(self, cid, group_id, name_suffix='', special_characters='', orphan=False):
|
||||
"""
|
||||
Create a problem
|
||||
Assign content group to the problem.
|
||||
"""
|
||||
vertical_parent_location = self.course.location
|
||||
if not orphan:
|
||||
subsection = ItemFactory.create(
|
||||
category='sequential',
|
||||
parent_location=self.course.location,
|
||||
display_name="Test Subsection {}".format(name_suffix)
|
||||
)
|
||||
vertical_parent_location = subsection.location
|
||||
|
||||
vertical = ItemFactory.create(
|
||||
category='vertical',
|
||||
parent_location=self.course.location,
|
||||
parent_location=vertical_parent_location,
|
||||
display_name="Test Unit {}".format(name_suffix)
|
||||
)
|
||||
|
||||
@@ -113,7 +142,7 @@ class HelperMethods(object):
|
||||
)
|
||||
|
||||
if not orphan:
|
||||
self.course.children.append(vertical.location)
|
||||
self.course.children.append(subsection.location)
|
||||
self.save_course()
|
||||
|
||||
return vertical, problem
|
||||
@@ -757,12 +786,108 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
|
||||
}]
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_can_get_correct_usage_info_for_split_test(self):
|
||||
"""
|
||||
When a split test is created and content group access is set for a problem within a group,
|
||||
the usage info should return a url to the split test, not to the group.
|
||||
"""
|
||||
# Create user partition for groups in the split test,
|
||||
# and another partition to set group access for the problem within the split test.
|
||||
self._add_user_partitions(count=1)
|
||||
self.course.user_partitions += [
|
||||
UserPartition(
|
||||
id=1,
|
||||
name='Cohort User Partition',
|
||||
scheme=UserPartition.get_scheme('cohort'),
|
||||
description='Cohort User Partition',
|
||||
groups=[
|
||||
Group(id=3, name="Problem Group")
|
||||
],
|
||||
),
|
||||
]
|
||||
self.store.update_item(self.course, ModuleStoreEnum.UserID.test)
|
||||
|
||||
__, split_test, problem = self._create_content_experiment(cid=0, name_suffix='0', group_id=3, cid_for_problem=1)
|
||||
|
||||
expected = {
|
||||
'id': 1,
|
||||
'name': 'Cohort User Partition',
|
||||
'scheme': 'cohort',
|
||||
'description': 'Cohort User Partition',
|
||||
'version': UserPartition.VERSION,
|
||||
'groups': [
|
||||
{'id': 3, 'name': 'Problem Group', 'version': 1, 'usage': [
|
||||
{
|
||||
'url': '/container/{}'.format(split_test.location),
|
||||
'label': 'Condition 1 vertical / Test Problem'
|
||||
}
|
||||
]},
|
||||
],
|
||||
u'parameters': {},
|
||||
u'active': True,
|
||||
}
|
||||
actual = self._get_user_partition('cohort')
|
||||
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_can_get_correct_usage_info_for_unit(self):
|
||||
"""
|
||||
When group access is set on the unit level, the usage info should return a url to the unit, not
|
||||
the sequential parent of the unit.
|
||||
"""
|
||||
self.course.user_partitions = [
|
||||
UserPartition(
|
||||
id=0,
|
||||
name='User Partition',
|
||||
scheme=UserPartition.get_scheme('cohort'),
|
||||
description='User Partition',
|
||||
groups=[
|
||||
Group(id=0, name="Group")
|
||||
],
|
||||
),
|
||||
]
|
||||
vertical, __ = self._create_problem_with_content_group(
|
||||
cid=0, group_id=0, name_suffix='0'
|
||||
)
|
||||
|
||||
self.client.ajax_post(
|
||||
reverse_usage_url("xblock_handler", vertical.location),
|
||||
data={'metadata': {'group_access': {0: [0]}}}
|
||||
)
|
||||
|
||||
actual = self._get_user_partition('cohort')
|
||||
expected = {
|
||||
'id': 0,
|
||||
'name': 'User Partition',
|
||||
'scheme': 'cohort',
|
||||
'description': 'User Partition',
|
||||
'version': UserPartition.VERSION,
|
||||
'groups': [
|
||||
{'id': 0, 'name': 'Group', 'version': 1, 'usage': [
|
||||
{
|
||||
'url': u"/container/{}".format(vertical.location),
|
||||
'label': u"Test Subsection 0 / Test Unit 0"
|
||||
},
|
||||
{
|
||||
'url': u"/container/{}".format(vertical.location),
|
||||
'label': u"Test Unit 0 / Test Problem 0"
|
||||
}
|
||||
]},
|
||||
],
|
||||
u'parameters': {},
|
||||
u'active': True,
|
||||
}
|
||||
|
||||
self.maxDiff = None
|
||||
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_can_get_correct_usage_info(self):
|
||||
"""
|
||||
Test if group configurations json updated successfully with usage information.
|
||||
"""
|
||||
self._add_user_partitions(count=2)
|
||||
vertical, __ = self._create_content_experiment(cid=0, name_suffix='0')
|
||||
__, split_test, __ = self._create_content_experiment(cid=0, name_suffix='0')
|
||||
self._create_content_experiment(name_suffix='1')
|
||||
|
||||
actual = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course)
|
||||
@@ -779,7 +904,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
|
||||
{'id': 2, 'name': 'Group C', 'version': 1},
|
||||
],
|
||||
'usage': [{
|
||||
'url': '/container/{}'.format(vertical.location),
|
||||
'url': '/container/{}'.format(split_test.location),
|
||||
'label': 'Test Unit 0 / Test Content Experiment 0',
|
||||
'validation': None,
|
||||
}],
|
||||
@@ -809,7 +934,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
|
||||
characters are being used in content experiment
|
||||
"""
|
||||
self._add_user_partitions(count=1)
|
||||
vertical, __ = self._create_content_experiment(cid=0, name_suffix='0', special_characters=u"JOSÉ ANDRÉS")
|
||||
__, split_test, __ = self._create_content_experiment(cid=0, name_suffix='0', special_characters=u"JOSÉ ANDRÉS")
|
||||
|
||||
actual = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course, )
|
||||
|
||||
@@ -825,7 +950,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
|
||||
{'id': 2, 'name': 'Group C', 'version': 1},
|
||||
],
|
||||
'usage': [{
|
||||
'url': '/container/{}'.format(vertical.location),
|
||||
'url': reverse_usage_url("container_handler", split_test.location),
|
||||
'label': u"Test Unit 0 / Test Content Experiment 0JOSÉ ANDRÉS",
|
||||
'validation': None,
|
||||
}],
|
||||
@@ -841,8 +966,8 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
|
||||
group configuration.
|
||||
"""
|
||||
self._add_user_partitions()
|
||||
vertical, __ = self._create_content_experiment(cid=0, name_suffix='0')
|
||||
vertical1, __ = self._create_content_experiment(cid=0, name_suffix='1')
|
||||
__, split_test, __ = self._create_content_experiment(cid=0, name_suffix='0')
|
||||
__, split_test1, __ = self._create_content_experiment(cid=0, name_suffix='1')
|
||||
|
||||
actual = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course)
|
||||
|
||||
@@ -858,11 +983,11 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
|
||||
{'id': 2, 'name': 'Group C', 'version': 1},
|
||||
],
|
||||
'usage': [{
|
||||
'url': '/container/{}'.format(vertical.location),
|
||||
'url': '/container/{}'.format(split_test.location),
|
||||
'label': 'Test Unit 0 / Test Content Experiment 0',
|
||||
'validation': None,
|
||||
}, {
|
||||
'url': '/container/{}'.format(vertical1.location),
|
||||
'url': '/container/{}'.format(split_test1.location),
|
||||
'label': 'Test Unit 1 / Test Content Experiment 1',
|
||||
'validation': None,
|
||||
}],
|
||||
|
||||
@@ -1,53 +1,60 @@
|
||||
"""Tests for items views."""
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import ddt
|
||||
|
||||
from mock import patch, Mock, PropertyMock
|
||||
from pytz import UTC
|
||||
from pyquery import PyQuery
|
||||
from webob import Response
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import Http404
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.core.urlresolvers import reverse
|
||||
from contentstore.utils import reverse_usage_url, reverse_course_url
|
||||
|
||||
from mock import Mock, PropertyMock, patch
|
||||
from opaque_keys import InvalidKeyError
|
||||
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
|
||||
from contentstore.views.component import (
|
||||
component_handler, get_component_templates
|
||||
)
|
||||
|
||||
from contentstore.views.item import (
|
||||
create_xblock_info, _get_source_index, _get_module_info, ALWAYS, VisibilityState, _xblock_type_and_display_name,
|
||||
add_container_page_publishing_info
|
||||
)
|
||||
from contentstore.tests.utils import CourseTestCase
|
||||
from student.tests.factories import UserFactory
|
||||
from xblock_django.models import XBlockConfiguration, XBlockStudioConfiguration, XBlockStudioConfigurationFlag
|
||||
from xmodule.capa_module import CapaDescriptor
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, TEST_DATA_SPLIT_MODULESTORE
|
||||
from xmodule.modulestore.tests.factories import ItemFactory, LibraryFactory, check_mongo_calls, CourseFactory
|
||||
from xmodule.x_module import STUDIO_VIEW, STUDENT_VIEW
|
||||
from xmodule.course_module import DEFAULT_START_DATE
|
||||
from opaque_keys.edx.keys import CourseKey, UsageKey
|
||||
from opaque_keys.edx.locations import Location
|
||||
from pyquery import PyQuery
|
||||
from pytz import UTC
|
||||
from webob import Response
|
||||
from xblock.core import XBlockAside
|
||||
from xblock.fields import Scope, String, ScopeIds
|
||||
from xblock.exceptions import NoSuchHandlerError
|
||||
from xblock.fields import Scope, ScopeIds, String
|
||||
from xblock.fragment import Fragment
|
||||
from xblock.runtime import DictKeyValueStore, KvsFieldData
|
||||
from xblock.test.tools import TestRuntime
|
||||
from xblock.exceptions import NoSuchHandlerError
|
||||
from xblock_django.user_service import DjangoXBlockUserService
|
||||
from opaque_keys.edx.keys import UsageKey, CourseKey
|
||||
from opaque_keys.edx.locations import Location
|
||||
from xmodule.partitions.partitions import (
|
||||
Group, UserPartition, ENROLLMENT_TRACK_PARTITION_ID, MINIMUM_STATIC_PARTITION_ID
|
||||
from xblock.validation import ValidationMessage
|
||||
|
||||
from contentstore.tests.utils import CourseTestCase
|
||||
from contentstore.utils import reverse_course_url, reverse_usage_url
|
||||
from contentstore.views.component import component_handler, get_component_templates
|
||||
from contentstore.views.item import (
|
||||
ALWAYS,
|
||||
VisibilityState,
|
||||
_get_module_info,
|
||||
_get_source_index,
|
||||
_xblock_type_and_display_name,
|
||||
add_container_page_publishing_info,
|
||||
create_xblock_info
|
||||
)
|
||||
from lms_xblock.mixin import NONSENSICAL_ACCESS_RESTRICTION
|
||||
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
|
||||
from student.tests.factories import UserFactory
|
||||
from xblock_django.models import XBlockConfiguration, XBlockStudioConfiguration, XBlockStudioConfigurationFlag
|
||||
from xblock_django.user_service import DjangoXBlockUserService
|
||||
from xmodule.capa_module import CapaDescriptor
|
||||
from xmodule.course_module import DEFAULT_START_DATE
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, LibraryFactory, check_mongo_calls
|
||||
from xmodule.partitions.partitions import (
|
||||
ENROLLMENT_TRACK_PARTITION_ID,
|
||||
MINIMUM_STATIC_PARTITION_ID,
|
||||
Group,
|
||||
UserPartition
|
||||
)
|
||||
from xmodule.partitions.tests.test_partitions import MockPartitionService
|
||||
from xmodule.x_module import STUDENT_VIEW, STUDIO_VIEW
|
||||
|
||||
|
||||
class AsideTest(XBlockAside):
|
||||
@@ -1155,6 +1162,64 @@ class TestMoveItem(ItemTest):
|
||||
response = json.loads(response.content)
|
||||
self.assertEqual(response['error'], 'Patch request did not recognise any parameters to handle.')
|
||||
|
||||
def _verify_validation_message(self, message, expected_message, expected_message_type):
|
||||
"""
|
||||
Verify that the validation message has the expected validation message and type.
|
||||
"""
|
||||
self.assertEqual(message.text, expected_message)
|
||||
self.assertEqual(message.type, expected_message_type)
|
||||
|
||||
def test_move_component_nonsensical_access_restriction_validation(self):
|
||||
"""
|
||||
Test that moving a component with non-contradicting access
|
||||
restrictions into a unit that has contradicting access
|
||||
restrictions brings up the nonsensical access validation
|
||||
message and that the message does not show up when moved
|
||||
into a unit where the component's access settings do not
|
||||
contradict the unit's access settings.
|
||||
"""
|
||||
group1 = self.course.user_partitions[0].groups[0]
|
||||
group2 = self.course.user_partitions[0].groups[1]
|
||||
vert2 = self.store.get_item(self.vert2_usage_key)
|
||||
html = self.store.get_item(self.html_usage_key)
|
||||
|
||||
# Inject mock partition service as obtaining the course from the draft modulestore
|
||||
# (which is the default for these tests) does not work.
|
||||
partitions_service = MockPartitionService(
|
||||
self.course,
|
||||
course_id=self.course.id,
|
||||
)
|
||||
html.runtime._services['partitions'] = partitions_service
|
||||
|
||||
# Set access settings so html will contradict vert2 when moved into that unit
|
||||
vert2.group_access = {self.course.user_partitions[0].id: [group1.id]}
|
||||
html.group_access = {self.course.user_partitions[0].id: [group2.id]}
|
||||
self.store.update_item(html, self.user.id)
|
||||
self.store.update_item(vert2, self.user.id)
|
||||
|
||||
# Verify that there is no warning when html is in a non contradicting unit
|
||||
validation = html.validate()
|
||||
self.assertEqual(len(validation.messages), 0)
|
||||
|
||||
# Now move it and confirm that the html component has been moved into vertical 2
|
||||
self.assert_move_item(self.html_usage_key, self.vert2_usage_key)
|
||||
html.parent = self.vert2_usage_key
|
||||
self.store.update_item(html, self.user.id)
|
||||
validation = html.validate()
|
||||
self.assertEqual(len(validation.messages), 1)
|
||||
self._verify_validation_message(
|
||||
validation.messages[0],
|
||||
NONSENSICAL_ACCESS_RESTRICTION,
|
||||
ValidationMessage.ERROR,
|
||||
)
|
||||
|
||||
# Move the html component back and confirm that the warning is gone again
|
||||
self.assert_move_item(self.html_usage_key, self.vert_usage_key)
|
||||
html.parent = self.vert_usage_key
|
||||
self.store.update_item(html, self.user.id)
|
||||
validation = html.validate()
|
||||
self.assertEqual(len(validation.messages), 0)
|
||||
|
||||
@patch('contentstore.views.item.log')
|
||||
def test_move_logging(self, mock_logger):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user