Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
ef581e11
Commit
ef581e11
authored
Jul 25, 2014
by
Andy Armstrong
Committed by
cahrens
Aug 07, 2014
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Apply code review comments and fix tests
parent
9b986049
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
208 additions
and
187 deletions
+208
-187
cms/djangoapps/contentstore/tests/utils.py
+14
-14
cms/djangoapps/contentstore/utils.py
+0
-14
cms/djangoapps/contentstore/views/component.py
+8
-4
cms/djangoapps/contentstore/views/item.py
+37
-29
cms/djangoapps/contentstore/views/tests/test_course_index.py
+3
-2
cms/djangoapps/contentstore/views/tests/test_item.py
+0
-0
cms/static/js/models/xblock_info.js
+8
-7
cms/static/js/spec/views/pages/container_subviews_spec.js
+0
-0
cms/static/js/spec/views/pages/course_outline_spec.js
+1
-5
cms/static/js/spec/views/unit_outline_spec.js
+6
-6
cms/static/js/views/baseview.js
+3
-0
cms/static/js/views/pages/container_subviews.js
+17
-13
cms/static/js/views/unit_outline.js
+0
-1
cms/static/js/views/utils/xblock_utils.js
+52
-2
cms/static/js/views/xblock_outline.js
+5
-0
cms/templates/course_outline.html
+1
-1
cms/templates/js/course-outline.underscore
+4
-15
cms/templates/js/publish-xblock.underscore
+11
-24
cms/templates/js/unit-outline.underscore
+1
-13
cms/templates/widgets/units.html
+1
-2
common/lib/xmodule/xmodule/modulestore/__init__.py
+6
-6
common/lib/xmodule/xmodule/modulestore/mixed.py
+3
-3
common/lib/xmodule/xmodule/modulestore/mongo/draft.py
+8
-8
common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py
+7
-7
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
+7
-7
common/test/acceptance/tests/base_studio_test.py
+2
-1
common/test/acceptance/tests/test_studio_general.py
+1
-1
common/test/acceptance/tests/test_studio_outline.py
+1
-1
common/test/acceptance/tests/test_studio_split_test.py
+1
-1
No files found.
cms/djangoapps/contentstore/tests/utils.py
View file @
ef581e11
...
@@ -9,7 +9,7 @@ from django.test.client import Client
...
@@ -9,7 +9,7 @@ from django.test.client import Client
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
xmodule.contentstore.django
import
contentstore
from
xmodule.contentstore.django
import
contentstore
from
xmodule.modulestore
import
Legacy
PublishState
,
ModuleStoreEnum
,
mongo
from
xmodule.modulestore
import
PublishState
,
ModuleStoreEnum
,
mongo
from
xmodule.modulestore.inheritance
import
own_metadata
from
xmodule.modulestore.inheritance
import
own_metadata
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
...
@@ -151,16 +151,16 @@ class CourseTestCase(ModuleStoreTestCase):
...
@@ -151,16 +151,16 @@ class CourseTestCase(ModuleStoreTestCase):
# create a Draft vertical
# create a Draft vertical
vertical
=
self
.
store
.
get_item
(
course_id
.
make_usage_key
(
'vertical'
,
self
.
TEST_VERTICAL
),
depth
=
1
)
vertical
=
self
.
store
.
get_item
(
course_id
.
make_usage_key
(
'vertical'
,
self
.
TEST_VERTICAL
),
depth
=
1
)
draft_vertical
=
self
.
store
.
convert_to_draft
(
vertical
.
location
,
self
.
user
.
id
)
draft_vertical
=
self
.
store
.
convert_to_draft
(
vertical
.
location
,
self
.
user
.
id
)
self
.
assertEqual
(
self
.
store
.
compute_publish_state
(
draft_vertical
),
Legacy
PublishState
.
draft
)
self
.
assertEqual
(
self
.
store
.
compute_publish_state
(
draft_vertical
),
PublishState
.
draft
)
# create a Private (draft only) vertical
# create a Private (draft only) vertical
private_vertical
=
self
.
store
.
create_item
(
self
.
user
.
id
,
course_id
,
'vertical'
,
self
.
PRIVATE_VERTICAL
)
private_vertical
=
self
.
store
.
create_item
(
self
.
user
.
id
,
course_id
,
'vertical'
,
self
.
PRIVATE_VERTICAL
)
self
.
assertEqual
(
self
.
store
.
compute_publish_state
(
private_vertical
),
Legacy
PublishState
.
private
)
self
.
assertEqual
(
self
.
store
.
compute_publish_state
(
private_vertical
),
PublishState
.
private
)
# create a Published (no draft) vertical
# create a Published (no draft) vertical
public_vertical
=
self
.
store
.
create_item
(
self
.
user
.
id
,
course_id
,
'vertical'
,
self
.
PUBLISHED_VERTICAL
)
public_vertical
=
self
.
store
.
create_item
(
self
.
user
.
id
,
course_id
,
'vertical'
,
self
.
PUBLISHED_VERTICAL
)
public_vertical
=
self
.
store
.
publish
(
public_vertical
.
location
,
self
.
user
.
id
)
public_vertical
=
self
.
store
.
publish
(
public_vertical
.
location
,
self
.
user
.
id
)
self
.
assertEqual
(
self
.
store
.
compute_publish_state
(
public_vertical
),
Legacy
PublishState
.
public
)
self
.
assertEqual
(
self
.
store
.
compute_publish_state
(
public_vertical
),
PublishState
.
public
)
# add the new private and new public as children of the sequential
# add the new private and new public as children of the sequential
sequential
=
self
.
store
.
get_item
(
course_id
.
make_usage_key
(
'sequential'
,
self
.
SEQUENTIAL
))
sequential
=
self
.
store
.
get_item
(
course_id
.
make_usage_key
(
'sequential'
,
self
.
SEQUENTIAL
))
...
@@ -197,7 +197,7 @@ class CourseTestCase(ModuleStoreTestCase):
...
@@ -197,7 +197,7 @@ class CourseTestCase(ModuleStoreTestCase):
def
verify_item_publish_state
(
item
,
publish_state
):
def
verify_item_publish_state
(
item
,
publish_state
):
"""Verifies the publish state of the item is as expected."""
"""Verifies the publish state of the item is as expected."""
if
publish_state
in
(
LegacyPublishState
.
private
,
Legacy
PublishState
.
draft
):
if
publish_state
in
(
PublishState
.
private
,
PublishState
.
draft
):
self
.
assertTrue
(
getattr
(
item
,
'is_draft'
,
False
))
self
.
assertTrue
(
getattr
(
item
,
'is_draft'
,
False
))
else
:
else
:
self
.
assertFalse
(
getattr
(
item
,
'is_draft'
,
False
))
self
.
assertFalse
(
getattr
(
item
,
'is_draft'
,
False
))
...
@@ -210,18 +210,18 @@ class CourseTestCase(ModuleStoreTestCase):
...
@@ -210,18 +210,18 @@ class CourseTestCase(ModuleStoreTestCase):
return
item
return
item
# verify that the draft vertical is draft
# verify that the draft vertical is draft
vertical
=
get_and_verify_publish_state
(
'vertical'
,
self
.
TEST_VERTICAL
,
Legacy
PublishState
.
draft
)
vertical
=
get_and_verify_publish_state
(
'vertical'
,
self
.
TEST_VERTICAL
,
PublishState
.
draft
)
for
child
in
vertical
.
get_children
():
for
child
in
vertical
.
get_children
():
verify_item_publish_state
(
child
,
Legacy
PublishState
.
draft
)
verify_item_publish_state
(
child
,
PublishState
.
draft
)
# make sure that we don't have a sequential that is not in draft mode
# make sure that we don't have a sequential that is not in draft mode
sequential
=
get_and_verify_publish_state
(
'sequential'
,
self
.
SEQUENTIAL
,
Legacy
PublishState
.
public
)
sequential
=
get_and_verify_publish_state
(
'sequential'
,
self
.
SEQUENTIAL
,
PublishState
.
public
)
# verify that we have the private vertical
# verify that we have the private vertical
private_vertical
=
get_and_verify_publish_state
(
'vertical'
,
self
.
PRIVATE_VERTICAL
,
Legacy
PublishState
.
private
)
private_vertical
=
get_and_verify_publish_state
(
'vertical'
,
self
.
PRIVATE_VERTICAL
,
PublishState
.
private
)
# verify that we have the public vertical
# verify that we have the public vertical
public_vertical
=
get_and_verify_publish_state
(
'vertical'
,
self
.
PUBLISHED_VERTICAL
,
Legacy
PublishState
.
public
)
public_vertical
=
get_and_verify_publish_state
(
'vertical'
,
self
.
PUBLISHED_VERTICAL
,
PublishState
.
public
)
# verify verticals are children of sequential
# verify verticals are children of sequential
for
vert
in
[
vertical
,
private_vertical
,
public_vertical
]:
for
vert
in
[
vertical
,
private_vertical
,
public_vertical
]:
...
@@ -332,7 +332,7 @@ class CourseTestCase(ModuleStoreTestCase):
...
@@ -332,7 +332,7 @@ class CourseTestCase(ModuleStoreTestCase):
it'll return public in that case
it'll return public in that case
"""
"""
supposed_state
=
self
.
store
.
compute_publish_state
(
item
)
supposed_state
=
self
.
store
.
compute_publish_state
(
item
)
if
supposed_state
==
Legacy
PublishState
.
draft
and
isinstance
(
item
.
runtime
.
modulestore
,
DraftModuleStore
):
if
supposed_state
==
PublishState
.
draft
and
isinstance
(
item
.
runtime
.
modulestore
,
DraftModuleStore
):
# see if the draft differs from the published
# see if the draft differs from the published
published
=
self
.
store
.
get_item
(
item
.
location
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
published_only
)
published
=
self
.
store
.
get_item
(
item
.
location
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
published_only
)
if
item
.
get_explicitly_set_fields_by_scope
()
!=
published
.
get_explicitly_set_fields_by_scope
():
if
item
.
get_explicitly_set_fields_by_scope
()
!=
published
.
get_explicitly_set_fields_by_scope
():
...
@@ -345,13 +345,13 @@ class CourseTestCase(ModuleStoreTestCase):
...
@@ -345,13 +345,13 @@ class CourseTestCase(ModuleStoreTestCase):
# checking children: if published differs from item, return draft
# checking children: if published differs from item, return draft
return
supposed_state
return
supposed_state
# published == item in all respects, so return public
# published == item in all respects, so return public
return
Legacy
PublishState
.
public
return
PublishState
.
public
elif
supposed_state
==
Legacy
PublishState
.
public
and
item
.
location
.
category
in
DIRECT_ONLY_CATEGORIES
:
elif
supposed_state
==
PublishState
.
public
and
item
.
location
.
category
in
DIRECT_ONLY_CATEGORIES
:
if
not
all
([
if
not
all
([
self
.
store
.
has_item
(
child_loc
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
draft_only
)
self
.
store
.
has_item
(
child_loc
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
draft_only
)
for
child_loc
in
item
.
children
for
child_loc
in
item
.
children
]):
]):
return
Legacy
PublishState
.
draft
return
PublishState
.
draft
else
:
else
:
return
supposed_state
return
supposed_state
else
:
else
:
...
...
cms/djangoapps/contentstore/utils.py
View file @
ef581e11
...
@@ -150,20 +150,6 @@ def course_image_url(course):
...
@@ -150,20 +150,6 @@ def course_image_url(course):
return
path
return
path
def
compute_publish_state
(
xblock
):
"""
Returns whether this xblock is draft, public, or private.
Returns:
LegacyPublishState.draft - content is in the process of being edited, but still has a previous
version deployed to LMS
LegacyPublishState.public - content is locked and deployed to LMS
LegacyPublishState.private - content is editable and not deployed to LMS
"""
return
modulestore
()
.
compute_publish_state
(
xblock
)
def
is_currently_visible_to_students
(
xblock
):
def
is_currently_visible_to_students
(
xblock
):
"""
"""
Returns true if there is a published version of the xblock that is currently visible to students.
Returns true if there is a published version of the xblock that is currently visible to students.
...
...
cms/djangoapps/contentstore/views/component.py
View file @
ef581e11
...
@@ -11,8 +11,8 @@ from django.conf import settings
...
@@ -11,8 +11,8 @@ from django.conf import settings
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
edxmako.shortcuts
import
render_to_response
from
edxmako.shortcuts
import
render_to_response
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore
import
LegacyPublishState
from
xblock.core
import
XBlock
from
xblock.core
import
XBlock
from
xblock.django.request
import
webob_to_django_response
,
django_to_webob_request
from
xblock.django.request
import
webob_to_django_response
,
django_to_webob_request
...
@@ -21,7 +21,7 @@ from xblock.fields import Scope
...
@@ -21,7 +21,7 @@ from xblock.fields import Scope
from
xblock.plugin
import
PluginMissingError
from
xblock.plugin
import
PluginMissingError
from
xblock.runtime
import
Mixologist
from
xblock.runtime
import
Mixologist
from
contentstore.utils
import
get_lms_link_for_item
,
compute_publish_state
from
contentstore.utils
import
get_lms_link_for_item
from
contentstore.views.helpers
import
get_parent_xblock
,
is_unit
,
xblock_type_display_name
from
contentstore.views.helpers
import
get_parent_xblock
,
is_unit
,
xblock_type_display_name
from
contentstore.views.item
import
create_xblock_info
from
contentstore.views.item
import
create_xblock_info
...
@@ -100,8 +100,8 @@ def subsection_handler(request, usage_key_string):
...
@@ -100,8 +100,8 @@ def subsection_handler(request, usage_key_string):
can_view_live
=
False
can_view_live
=
False
subsection_units
=
item
.
get_children
()
subsection_units
=
item
.
get_children
()
for
unit
in
subsection_units
:
for
unit
in
subsection_units
:
state
=
compute_publish_state
(
unit
)
has_published
=
modulestore
()
.
has_item
(
unit
.
location
,
revision
=
ModuleStoreEnum
.
RevisionOption
.
published_only
)
if
state
in
(
LegacyPublishState
.
public
,
LegacyPublishState
.
draft
)
:
if
has_published
:
can_view_live
=
True
can_view_live
=
True
break
break
...
@@ -178,6 +178,10 @@ def container_handler(request, usage_key_string):
...
@@ -178,6 +178,10 @@ def container_handler(request, usage_key_string):
# about the block's ancestors and siblings for use by the Unit Outline.
# about the block's ancestors and siblings for use by the Unit Outline.
xblock_info
=
create_xblock_info
(
xblock
,
include_ancestor_info
=
is_unit_page
)
xblock_info
=
create_xblock_info
(
xblock
,
include_ancestor_info
=
is_unit_page
)
# On the unit page only, add 'has_changes' to indicate when there are changes that can be discarded.
if
is_unit_page
:
xblock_info
[
'has_changes'
]
=
modulestore
()
.
has_changes
(
xblock
.
location
)
# Create the link for preview.
# Create the link for preview.
preview_lms_base
=
settings
.
FEATURES
.
get
(
'PREVIEW_LMS_BASE'
)
preview_lms_base
=
settings
.
FEATURES
.
get
(
'PREVIEW_LMS_BASE'
)
# need to figure out where this item is in the list of children as the
# need to figure out where this item is in the list of children as the
...
...
cms/djangoapps/contentstore/views/item.py
View file @
ef581e11
...
@@ -636,9 +636,9 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
...
@@ -636,9 +636,9 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
"id"
:
unicode
(
xblock
.
location
),
"id"
:
unicode
(
xblock
.
location
),
"display_name"
:
xblock
.
display_name_with_default
,
"display_name"
:
xblock
.
display_name_with_default
,
"category"
:
xblock
.
category
,
"category"
:
xblock
.
category
,
"published"
:
published
,
"edited_on"
:
get_default_time_display
(
xblock
.
subtree_edited_on
)
if
xblock
.
subtree_edited_on
else
None
,
"edited_on"
:
get_default_time_display
(
xblock
.
subtree_edited_on
)
if
xblock
.
subtree_edited_on
else
None
,
"edited_by"
:
safe_get_username
(
xblock
.
subtree_edited_by
),
"edited_by"
:
safe_get_username
(
xblock
.
subtree_edited_by
),
"published"
:
published
,
"published_on"
:
get_default_time_display
(
xblock
.
published_date
)
if
xblock
.
published_date
else
None
,
"published_on"
:
get_default_time_display
(
xblock
.
published_date
)
if
xblock
.
published_date
else
None
,
"published_by"
:
safe_get_username
(
xblock
.
published_by
),
"published_by"
:
safe_get_username
(
xblock
.
published_by
),
'studio_url'
:
xblock_studio_url
(
xblock
),
'studio_url'
:
xblock_studio_url
(
xblock
),
...
@@ -646,7 +646,7 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
...
@@ -646,7 +646,7 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
"release_date"
:
release_date
,
"release_date"
:
release_date
,
"release_date_from"
:
_get_release_date_from
(
xblock
)
if
release_date
else
None
,
"release_date_from"
:
_get_release_date_from
(
xblock
)
if
release_date
else
None
,
"currently_visible_to_students"
:
currently_visible_to_students
,
"currently_visible_to_students"
:
currently_visible_to_students
,
"
publish_state"
:
_compute_publish
_state
(
xblock
,
child_info
)
if
not
xblock
.
category
==
'course'
else
None
"
visibility_state"
:
_compute_visibility
_state
(
xblock
,
child_info
)
if
not
xblock
.
category
==
'course'
else
None
}
}
if
data
is
not
None
:
if
data
is
not
None
:
xblock_info
[
"data"
]
=
data
xblock_info
[
"data"
]
=
data
...
@@ -659,63 +659,71 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
...
@@ -659,63 +659,71 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
return
xblock_info
return
xblock_info
class
Publish
State
(
object
):
class
Visibility
State
(
object
):
"""
"""
Represents the possible publish states for an xblock:
Represents the possible visibility states for an xblock:
live - the block and all of its children are live to students (except for staff only items)
ready - the block and all of its children are ready to go live in the future
live - the block and all of its descendants are live to students (excluding staff only items)
unscheduled - the block and all of its children are unscheduled
Note: Live means both published and released.
has_unpublished_content - the block or its children have unpublished content that is not staff only
ready - the block is ready to go live and all of its descendants are live or ready (excluding staff only items)
Note: content is ready when it is published and scheduled with a release date in the future.
unscheduled - the block and all of its descendants have no release date (excluding staff only items)
Note: it is valid for items to be published with no release date in which case they are still unscheduled.
needs_attention - the block or its descendants are not fully live, ready or unscheduled (excluding staff only items)
For example: one subsection has draft content, or there's both unreleased and released content in one section.
staff_only - all of the block's content is to be shown to staff only
staff_only - all of the block's content is to be shown to staff only
Note: staff only items do not affect their parent's state.
"""
"""
live
=
'live'
live
=
'live'
ready
=
'ready'
ready
=
'ready'
unscheduled
=
'unscheduled'
unscheduled
=
'unscheduled'
# unscheduled
has_unpublished_content
=
'has_unpublished_content
'
needs_attention
=
'needs_attention
'
staff_only
=
'staff_only'
staff_only
=
'staff_only'
def
_compute_
publish
_state
(
xblock
,
child_info
):
def
_compute_
visibility
_state
(
xblock
,
child_info
):
"""
"""
Returns the current publish state for the specified xblock and its children
Returns the current publish state for the specified xblock and its children
"""
"""
if
xblock
.
visible_to_staff_only
:
if
xblock
.
visible_to_staff_only
:
return
Publish
State
.
staff_only
return
Visibility
State
.
staff_only
elif
is_unit
(
xblock
)
and
modulestore
()
.
has_changes
(
xblock
.
location
):
elif
is_unit
(
xblock
)
and
modulestore
()
.
has_changes
(
xblock
.
location
):
return
PublishState
.
has_unpublished_content
return
VisibilityState
.
needs_attention
is_unscheduled
=
xblock
.
start
==
DEFAULT_START_DATE
is_unscheduled
=
xblock
.
start
==
DEFAULT_START_DATE
is_live
=
datetime
.
now
(
UTC
)
>
xblock
.
start
children
=
child_info
and
child_info
[
'children'
]
children
=
child_info
and
child_info
[
'children'
]
if
children
and
len
(
children
)
>
0
:
if
children
and
len
(
children
)
>
0
:
all_staff_only
=
True
all_staff_only
=
True
all_unscheduled
=
True
all_unscheduled
=
True
all_live
=
True
all_live
=
True
for
child
in
child_info
[
'children'
]:
for
child
in
child_info
[
'children'
]:
child_state
=
child
[
'
publish
_state'
]
child_state
=
child
[
'
visibility
_state'
]
if
child_state
==
PublishState
.
has_unpublished_content
:
if
child_state
==
VisibilityState
.
needs_attention
:
return
child_state
return
child_state
elif
not
child_state
==
Publish
State
.
staff_only
:
elif
not
child_state
==
Visibility
State
.
staff_only
:
all_staff_only
=
False
all_staff_only
=
False
if
not
child_state
==
Publish
State
.
unscheduled
:
if
not
child_state
==
Visibility
State
.
unscheduled
:
all_unscheduled
=
False
all_unscheduled
=
False
if
not
child_state
==
Publish
State
.
live
:
if
not
child_state
==
Visibility
State
.
live
:
all_live
=
False
all_live
=
False
if
all_staff_only
:
if
all_staff_only
:
return
Publish
State
.
staff_only
return
Visibility
State
.
staff_only
elif
all_unscheduled
:
elif
all_unscheduled
:
if
not
is_unscheduled
:
return
VisibilityState
.
unscheduled
if
is_unscheduled
else
VisibilityState
.
needs_attention
return
PublishState
.
has_unpublished_content
else
:
return
PublishState
.
unscheduled
elif
all_live
:
elif
all_live
:
return
PublishState
.
live
return
VisibilityState
.
live
if
is_live
else
VisibilityState
.
needs_attention
else
:
else
:
return
PublishState
.
ready
return
VisibilityState
.
ready
if
not
is_unscheduled
else
VisibilityState
.
needs_attention
if
is_unscheduled
:
if
is_unscheduled
:
return
Publish
State
.
unscheduled
return
Visibility
State
.
unscheduled
elif
datetime
.
now
(
UTC
)
>
xblock
.
start
:
elif
is_live
:
return
Publish
State
.
live
return
Visibility
State
.
live
else
:
else
:
return
Publish
State
.
ready
return
Visibility
State
.
ready
def
_create_xblock_ancestor_info
(
xblock
):
def
_create_xblock_ancestor_info
(
xblock
):
...
...
cms/djangoapps/contentstore/views/tests/test_course_index.py
View file @
ef581e11
...
@@ -8,6 +8,7 @@ from contentstore.tests.utils import CourseTestCase
...
@@ -8,6 +8,7 @@ from contentstore.tests.utils import CourseTestCase
from
contentstore.utils
import
reverse_course_url
,
add_instructor
from
contentstore.utils
import
reverse_course_url
,
add_instructor
from
contentstore.views.access
import
has_course_access
from
contentstore.views.access
import
has_course_access
from
contentstore.views.course
import
course_outline_initial_state
from
contentstore.views.course
import
course_outline_initial_state
from
contentstore.views.item
import
create_xblock_info
,
VisibilityState
from
course_action_state.models
import
CourseRerunState
from
course_action_state.models
import
CourseRerunState
from
contentstore.views.item
import
create_xblock_info
from
contentstore.views.item
import
create_xblock_info
from
contentstore.views.item
import
create_xblock_info
,
PublishState
from
contentstore.views.item
import
create_xblock_info
,
PublishState
...
@@ -230,7 +231,7 @@ class TestCourseOutline(CourseTestCase):
...
@@ -230,7 +231,7 @@ class TestCourseOutline(CourseTestCase):
self
.
assertEqual
(
json_response
[
'category'
],
'course'
)
self
.
assertEqual
(
json_response
[
'category'
],
'course'
)
self
.
assertEqual
(
json_response
[
'id'
],
'i4x://MITx/999/course/Robot_Super_Course'
)
self
.
assertEqual
(
json_response
[
'id'
],
'i4x://MITx/999/course/Robot_Super_Course'
)
self
.
assertEqual
(
json_response
[
'display_name'
],
'Robot Super Course'
)
self
.
assertEqual
(
json_response
[
'display_name'
],
'Robot Super Course'
)
self
.
assertIsNone
(
json_response
[
'
publish
_state'
])
self
.
assertIsNone
(
json_response
[
'
visibility
_state'
])
# Now verify the first child
# Now verify the first child
children
=
json_response
[
'child_info'
][
'children'
]
children
=
json_response
[
'child_info'
][
'children'
]
...
@@ -239,7 +240,7 @@ class TestCourseOutline(CourseTestCase):
...
@@ -239,7 +240,7 @@ class TestCourseOutline(CourseTestCase):
self
.
assertEqual
(
first_child_response
[
'category'
],
'chapter'
)
self
.
assertEqual
(
first_child_response
[
'category'
],
'chapter'
)
self
.
assertEqual
(
first_child_response
[
'id'
],
'i4x://MITx/999/chapter/Week_1'
)
self
.
assertEqual
(
first_child_response
[
'id'
],
'i4x://MITx/999/chapter/Week_1'
)
self
.
assertEqual
(
first_child_response
[
'display_name'
],
'Week 1'
)
self
.
assertEqual
(
first_child_response
[
'display_name'
],
'Week 1'
)
self
.
assertEqual
(
first_child_response
[
'
publish_state'
],
Publish
State
.
unscheduled
)
self
.
assertEqual
(
first_child_response
[
'
visibility_state'
],
Visibility
State
.
unscheduled
)
self
.
assertTrue
(
len
(
first_child_response
[
'child_info'
][
'children'
])
>
0
)
self
.
assertTrue
(
len
(
first_child_response
[
'child_info'
][
'children'
])
>
0
)
# Finally, validate the entire response for consistency
# Finally, validate the entire response for consistency
...
...
cms/djangoapps/contentstore/views/tests/test_item.py
View file @
ef581e11
This diff is collapsed.
Click to expand it.
cms/static/js/models/xblock_info.js
View file @
ef581e11
...
@@ -45,14 +45,15 @@ define(["backbone", "underscore", "js/utils/module"], function(Backbone, _, Modu
...
@@ -45,14 +45,15 @@ define(["backbone", "underscore", "js/utils/module"], function(Backbone, _, Modu
*/
*/
"published_by"
:
null
,
"published_by"
:
null
,
/**
/**
* Represents the possible publish states for an xblock:
* True if the xblock has changes.
* is_live - the block and all of its children are live to students (except for staff only items)
* Note: this is not always provided as a performance optimization.
* is_ready - the block and all of its children are ready to go live in the future
* unscheduled - the block and all of its children are unscheduled
* has_unpublished_content - the block or its children have unpublished content that is not staff only
* is_staff_only - all of the block's content is to be shown to staff only
*/
*/
"publish_state"
:
null
,
"has_changes"
:
null
,
/**
* Represents the possible publish states for an xblock. See the documentation
* for XBlockVisibility to see a comprehensive enumeration of the states.
*/
"visibility_state"
:
null
,
/**
/**
* True iff the release date of the xblock is in the past.
* True iff the release date of the xblock is in the past.
*/
*/
...
...
cms/static/js/spec/views/pages/container_subviews_spec.js
View file @
ef581e11
This diff is collapsed.
Click to expand it.
cms/static/js/spec/views/pages/course_outline_spec.js
View file @
ef581e11
...
@@ -118,7 +118,7 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
...
@@ -118,7 +118,7 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
category
:
'vertical'
,
category
:
'vertical'
,
studio_url
:
'/container/mock-unit'
,
studio_url
:
'/container/mock-unit'
,
is_container
:
true
,
is_container
:
true
,
publish
_state
:
'unscheduled'
,
visibility
_state
:
'unscheduled'
,
edited_on
:
'Jul 02, 2014 at 20:56 UTC'
,
edited_on
:
'Jul 02, 2014 at 20:56 UTC'
,
edited_by
:
'MockUser'
edited_by
:
'MockUser'
}])
}])
...
@@ -432,9 +432,5 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
...
@@ -432,9 +432,5 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
expect
(
unitAnchor
.
attr
(
'href'
)).
toBe
(
'/container/mock-unit'
);
expect
(
unitAnchor
.
attr
(
'href'
)).
toBe
(
'/container/mock-unit'
);
});
});
});
});
describe
(
"Publishing State"
,
function
()
{
// TODO: implement this!!!!
});
});
});
});
});
cms/static/js/spec/views/unit_outline_spec.js
View file @
ef581e11
...
@@ -25,14 +25,14 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
...
@@ -25,14 +25,14 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
category
:
'vertical'
,
category
:
'vertical'
,
display_name
:
displayName
,
display_name
:
displayName
,
studio_url
:
'/container/mock-unit'
,
studio_url
:
'/container/mock-unit'
,
publish
_state
:
'unscheduled'
,
visibility
_state
:
'unscheduled'
,
ancestor_info
:
{
ancestor_info
:
{
ancestors
:
[{
ancestors
:
[{
id
:
'mock-subsection'
,
id
:
'mock-subsection'
,
category
:
'sequential'
,
category
:
'sequential'
,
display_name
:
'Mock Subsection'
,
display_name
:
'Mock Subsection'
,
studio_url
:
'/course/mock-course?show=mock-subsection'
,
studio_url
:
'/course/mock-course?show=mock-subsection'
,
publish
_state
:
'unscheduled'
,
visibility
_state
:
'unscheduled'
,
child_info
:
{
child_info
:
{
category
:
'vertical'
,
category
:
'vertical'
,
display_name
:
'Unit'
,
display_name
:
'Unit'
,
...
@@ -41,13 +41,13 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
...
@@ -41,13 +41,13 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
category
:
'vertical'
,
category
:
'vertical'
,
display_name
:
displayName
,
display_name
:
displayName
,
studio_url
:
'/container/mock-unit'
,
studio_url
:
'/container/mock-unit'
,
publish
_state
:
'unscheduled'
visibility
_state
:
'unscheduled'
},
{
},
{
id
:
'mock-unit-2'
,
id
:
'mock-unit-2'
,
category
:
'vertical'
,
category
:
'vertical'
,
display_name
:
'Mock Unit 2'
,
display_name
:
'Mock Unit 2'
,
studio_url
:
'/container/mock-unit-2'
,
studio_url
:
'/container/mock-unit-2'
,
publish
_state
:
'unscheduled'
visibility
_state
:
'unscheduled'
}]
}]
}
}
},
{
},
{
...
@@ -55,13 +55,13 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
...
@@ -55,13 +55,13 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers"
category
:
'chapter'
,
category
:
'chapter'
,
display_name
:
'Section'
,
display_name
:
'Section'
,
studio_url
:
'/course/slashes:mock-course?show=mock-section'
,
studio_url
:
'/course/slashes:mock-course?show=mock-section'
,
publish
_state
:
'unscheduled'
visibility
_state
:
'unscheduled'
},
{
},
{
id
:
'mock-course'
,
id
:
'mock-course'
,
category
:
'course'
,
category
:
'course'
,
display_name
:
'Mock Course'
,
display_name
:
'Mock Course'
,
studio_url
:
'/course/mock-course'
,
studio_url
:
'/course/mock-course'
,
publish
_state
:
'unscheduled'
visibility
_state
:
'unscheduled'
}]
}]
},
},
metadata
:
{
metadata
:
{
...
...
cms/static/js/views/baseview.js
View file @
ef581e11
...
@@ -17,6 +17,9 @@ define(["jquery", "underscore", "backbone", "gettext", "js/utils/handle_iframe_b
...
@@ -17,6 +17,9 @@ define(["jquery", "underscore", "backbone", "gettext", "js/utils/handle_iframe_b
},
},
options
:
{
options
:
{
// UX is moving towards using 'is-collapsed' in preference over 'collapsed',
// but use the old scheme as the default so that existing code doesn't need
// to be rewritten.
collapsedClass
:
'collapsed'
collapsedClass
:
'collapsed'
},
},
...
...
cms/static/js/views/pages/container_subviews.js
View file @
ef581e11
/**
/**
* Subviews (usually small side panels) for XBlockContainerPage.
* Subviews (usually small side panels) for XBlockContainerPage.
*/
*/
define
([
"jquery"
,
"underscore"
,
"gettext"
,
"js/views/baseview"
,
"js/views/utils/view_utils"
],
define
([
"jquery"
,
"underscore"
,
"gettext"
,
"js/views/baseview"
,
"js/views/utils/view_utils"
,
function
(
$
,
_
,
gettext
,
BaseView
,
ViewUtils
)
{
"js/views/utils/xblock_utils"
],
function
(
$
,
_
,
gettext
,
BaseView
,
ViewUtils
,
XBlockViewUtils
)
{
var
disabledCss
=
"is-disabled"
;
var
VisibilityState
=
XBlockViewUtils
.
VisibilityState
,
disabledCss
=
"is-disabled"
;
/**
/**
* A view that refreshes the view when certain values in the XBlockInfo have changed
* A view that refreshes the view when certain values in the XBlockInfo have changed
...
@@ -53,20 +54,19 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
...
@@ -53,20 +54,19 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
*/
*/
var
PreviewActionController
=
ContainerStateListenerView
.
extend
({
var
PreviewActionController
=
ContainerStateListenerView
.
extend
({
shouldRefresh
:
function
(
model
)
{
shouldRefresh
:
function
(
model
)
{
return
ViewUtils
.
hasChangedAttributes
(
model
,
[
'
edited_on'
,
'published_on'
,
'publish_state
'
]);
return
ViewUtils
.
hasChangedAttributes
(
model
,
[
'
has_changes'
,
'published
'
]);
},
},
render
:
function
()
{
render
:
function
()
{
var
previewAction
=
this
.
$el
.
find
(
'.button-preview'
),
var
previewAction
=
this
.
$el
.
find
(
'.button-preview'
),
viewLiveAction
=
this
.
$el
.
find
(
'.button-view'
),
viewLiveAction
=
this
.
$el
.
find
(
'.button-view'
);
publishState
=
this
.
model
.
get
(
'publish_state'
);
if
(
this
.
model
.
get
(
'published'
))
{
if
(
publishState
!==
'unscheduled'
)
{
viewLiveAction
.
removeClass
(
disabledCss
);
viewLiveAction
.
removeClass
(
disabledCss
);
}
}
else
{
else
{
viewLiveAction
.
addClass
(
disabledCss
);
viewLiveAction
.
addClass
(
disabledCss
);
}
}
if
(
publishState
!==
'live'
&&
publishState
!==
'ready'
)
{
if
(
this
.
model
.
get
(
'has_changes'
)
||
!
this
.
model
.
get
(
'published'
)
)
{
previewAction
.
removeClass
(
disabledCss
);
previewAction
.
removeClass
(
disabledCss
);
}
}
else
{
else
{
...
@@ -99,14 +99,18 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
...
@@ -99,14 +99,18 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
},
},
onSync
:
function
(
model
)
{
onSync
:
function
(
model
)
{
if
(
ViewUtils
.
hasChangedAttributes
(
model
,
[
'edited_on'
,
'published_on'
,
'publish_state'
]))
{
if
(
ViewUtils
.
hasChangedAttributes
(
model
,
[
'has_changes'
,
'published'
,
'edited_on'
,
'edited_by'
,
'visibility_state'
]))
{
this
.
render
();
this
.
render
();
}
}
},
},
render
:
function
()
{
render
:
function
()
{
this
.
$el
.
html
(
this
.
template
({
this
.
$el
.
html
(
this
.
template
({
publishState
:
this
.
model
.
get
(
'publish_state'
),
visibilityState
:
this
.
model
.
get
(
'visibility_state'
),
visibilityClass
:
XBlockViewUtils
.
getXBlockVisibilityClass
(
this
.
model
.
get
(
'visibility_state'
)),
hasChanges
:
this
.
model
.
get
(
'has_changes'
),
editedOn
:
this
.
model
.
get
(
'edited_on'
),
editedOn
:
this
.
model
.
get
(
'edited_on'
),
editedBy
:
this
.
model
.
get
(
'edited_by'
),
editedBy
:
this
.
model
.
get
(
'edited_by'
),
published
:
this
.
model
.
get
(
'published'
),
published
:
this
.
model
.
get
(
'published'
),
...
@@ -162,7 +166,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
...
@@ -162,7 +166,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
if
(
e
&&
e
.
preventDefault
)
{
if
(
e
&&
e
.
preventDefault
)
{
e
.
preventDefault
();
e
.
preventDefault
();
}
}
enableStaffLock
=
xblockInfo
.
get
(
'
publish_state'
)
!==
'staff_only'
;
enableStaffLock
=
xblockInfo
.
get
(
'
visibility_state'
)
!==
VisibilityState
.
staffOnly
;
revertCheckBox
=
function
()
{
revertCheckBox
=
function
()
{
self
.
checkStaffLock
(
!
enableStaffLock
);
self
.
checkStaffLock
(
!
enableStaffLock
);
...
@@ -221,7 +225,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
...
@@ -221,7 +225,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
},
},
onSync
:
function
(
model
)
{
onSync
:
function
(
model
)
{
if
(
ViewUtils
.
hasChangedAttributes
(
model
,
[
'published
_on
'
]))
{
if
(
ViewUtils
.
hasChangedAttributes
(
model
,
[
'published
'
,
'published_on'
,
'published_by
'
]))
{
this
.
render
();
this
.
render
();
}
}
},
},
...
...
cms/static/js/views/unit_outline.js
View file @
ef581e11
...
@@ -10,7 +10,6 @@ define(['js/views/xblock_outline'],
...
@@ -10,7 +10,6 @@ define(['js/views/xblock_outline'],
// takes XBlockInfo as a model
// takes XBlockInfo as a model
templateName
:
'unit-outline'
,
templateName
:
'unit-outline'
,
className
:
'group-configurations-list'
,
render
:
function
()
{
render
:
function
()
{
XBlockOutlineView
.
prototype
.
render
.
call
(
this
);
XBlockOutlineView
.
prototype
.
render
.
call
(
this
);
...
...
cms/static/js/views/utils/xblock_utils.js
View file @
ef581e11
...
@@ -3,7 +3,36 @@
...
@@ -3,7 +3,36 @@
*/
*/
define
([
"jquery"
,
"underscore"
,
"gettext"
,
"js/views/utils/view_utils"
,
"js/utils/module"
],
define
([
"jquery"
,
"underscore"
,
"gettext"
,
"js/views/utils/view_utils"
,
"js/utils/module"
],
function
(
$
,
_
,
gettext
,
ViewUtils
,
ModuleUtils
)
{
function
(
$
,
_
,
gettext
,
ViewUtils
,
ModuleUtils
)
{
var
addXBlock
,
deleteXBlock
,
createUpdateRequestData
,
updateXBlockField
;
var
addXBlock
,
deleteXBlock
,
createUpdateRequestData
,
updateXBlockField
,
VisibilityState
,
getXBlockVisibilityClass
;
/**
* Represents the possible visibility states for an xblock:
*
* live - the block and all of its descendants are live to students (excluding staff only)
* Note: Live means both published and released.
*
* ready - the block is ready to go live and all of its descendants are live or ready (excluding staff only)
* Note: content is ready when it is published and scheduled with a release date in the future.
*
* unscheduled - the block and all of its descendants have no release date (excluding staff only)
* Note: it is valid for items to be published with no release date in which case they are unscheduled.
*
* needsAttention - the block or its descendants need attention
* i.e. there is some content that is not fully live, ready, unscheduled or staff only.
* For example: one subsection has draft content, or there's both unreleased and released content
* in one section.
*
* staffOnly - all of the block's content is to be shown to staff only
* Note: staff only items do not affect their parent's state.
*/
VisibilityState
=
{
live
:
'live'
,
ready
:
'ready'
,
unscheduled
:
'unscheduled'
,
needsAttention
:
'needs_attention'
,
staffOnly
:
'staff_only'
};
/**
/**
* Adds an xblock based upon the data attributes of the specified add button. A promise
* Adds an xblock based upon the data attributes of the specified add button. A promise
...
@@ -83,9 +112,30 @@ define(["jquery", "underscore", "gettext", "js/views/utils/view_utils", "js/util
...
@@ -83,9 +112,30 @@ define(["jquery", "underscore", "gettext", "js/views/utils/view_utils", "js/util
});
});
};
};
/**
* Returns the CSS class to represent the specified xblock visibility state.
*/
getXBlockVisibilityClass
=
function
(
visibilityState
)
{
if
(
visibilityState
===
VisibilityState
.
staffOnly
)
{
return
'is-staff-only'
;
}
if
(
visibilityState
===
VisibilityState
.
live
)
{
return
'is-live'
;
}
if
(
visibilityState
===
VisibilityState
.
ready
)
{
return
'is-ready'
;
}
if
(
visibilityState
===
VisibilityState
.
needsAttention
)
{
return
'has-warnings'
;
}
return
''
;
};
return
{
return
{
'VisibilityState'
:
VisibilityState
,
'addXBlock'
:
addXBlock
,
'addXBlock'
:
addXBlock
,
'deleteXBlock'
:
deleteXBlock
,
'deleteXBlock'
:
deleteXBlock
,
'updateXBlockField'
:
updateXBlockField
'updateXBlockField'
:
updateXBlockField
,
'getXBlockVisibilityClass'
:
getXBlockVisibilityClass
};
};
});
});
cms/static/js/views/xblock_outline.js
View file @
ef581e11
...
@@ -68,6 +68,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
...
@@ -68,6 +68,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
}
}
html
=
this
.
template
({
html
=
this
.
template
({
xblockInfo
:
xblockInfo
,
xblockInfo
:
xblockInfo
,
visibilityClass
:
XBlockViewUtils
.
getXBlockVisibilityClass
(
xblockInfo
.
get
(
'visibility_state'
)),
parentInfo
:
this
.
parentInfo
,
parentInfo
:
this
.
parentInfo
,
xblockType
:
xblockType
,
xblockType
:
xblockType
,
parentType
:
parentType
,
parentType
:
parentType
,
...
@@ -200,7 +201,11 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
...
@@ -200,7 +201,11 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/
}
else
{
}
else
{
locatorElement
=
this
.
$
(
'.outline-item[data-locator="'
+
locatorToShow
+
'"]'
);
locatorElement
=
this
.
$
(
'.outline-item[data-locator="'
+
locatorToShow
+
'"]'
);
}
}
if
(
locatorElement
.
length
>
0
)
{
ViewUtils
.
setScrollOffset
(
locatorElement
,
scrollOffset
);
ViewUtils
.
setScrollOffset
(
locatorElement
,
scrollOffset
);
}
else
{
console
.
error
(
"Failed to show item with locator "
+
locatorToShow
+
""
);
}
if
(
editDisplayName
)
{
if
(
editDisplayName
)
{
locatorElement
.
find
(
'> div[class$="header"] .xblock-field-value-edit'
).
click
();
locatorElement
.
find
(
'> div[class$="header"] .xblock-field-value-edit'
).
click
();
}
}
...
...
cms/templates/course_outline.html
View file @
ef581e11
...
@@ -72,7 +72,7 @@ from contentstore.utils import reverse_usage_url
...
@@ -72,7 +72,7 @@ from contentstore.utils import reverse_usage_url
<
%
<
%
course_locator =
context_course.location
course_locator =
context_course.location
%
>
%
>
<article
class=
"outline"
data-locator=
"${course_locator}"
data-course-key=
"${course_locator.course_key}"
>
<article
class=
"outline
outline-course
"
data-locator=
"${course_locator}"
data-course-key=
"${course_locator.course_key}"
>
</article>
</article>
</div>
</div>
<div
class=
"ui-loading"
>
<div
class=
"ui-loading"
>
...
...
cms/templates/js/course-outline.underscore
View file @
ef581e11
<%
<%
var category = xblockInfo.get('category');
var category = xblockInfo.get('category');
var releasedToStudents = xblockInfo.get('released_to_students');
var releasedToStudents = xblockInfo.get('released_to_students');
var publishState = xblockInfo.get('publish_state');
var visibilityState = xblockInfo.get('visibility_state');
var publishClass = '';
if (publishState === 'staff_only') {
publishClass = 'is-staff-only';
} else if (publishState === 'live') {
publishClass = 'is-live';
} else if (publishState === 'ready') {
publishClass = 'is-ready';
} else if (publishState === 'has_unpublished_content') {
publishClass = 'has-warnings';
}
var listType = 'list-unknown';
var listType = 'list-unknown';
if (xblockType === 'course') {
if (xblockType === 'course') {
...
@@ -25,10 +14,10 @@ if (xblockType === 'course') {
...
@@ -25,10 +14,10 @@ if (xblockType === 'course') {
var statusMessage = null;
var statusMessage = null;
var statusType = null;
var statusType = null;
if (
publish
State === 'staff_only') {
if (
visibility
State === 'staff_only') {
statusType = 'staff-only';
statusType = 'staff-only';
statusMessage = 'Contains staff only content';
statusMessage = 'Contains staff only content';
} else if (
publishState === 'has_unpublished_content
') {
} else if (
visibilityState === 'needs_attention
') {
if (category === 'vertical') {
if (category === 'vertical') {
statusType = 'warning';
statusType = 'warning';
if (releasedToStudents) {
if (releasedToStudents) {
...
@@ -49,7 +38,7 @@ if (statusType === 'warning') {
...
@@ -49,7 +38,7 @@ if (statusType === 'warning') {
}
}
%>
%>
<% if (parentInfo) { %>
<% if (parentInfo) { %>
<li class="outline-item outline-<%= xblockType %> <%=
publish
Class %> is-draggable <%= includesChildren ? 'is-collapsible' : '' %> <%= isCollapsed ? 'is-collapsed' : '' %>"
<li class="outline-item outline-<%= xblockType %> <%=
visibility
Class %> is-draggable <%= includesChildren ? 'is-collapsible' : '' %> <%= isCollapsed ? 'is-collapsed' : '' %>"
data-parent="<%= parentInfo.get('id') %>" data-locator="<%= xblockInfo.get('id') %>">
data-parent="<%= parentInfo.get('id') %>" data-locator="<%= xblockInfo.get('id') %>">
<div class="<%= xblockType %>-header">
<div class="<%= xblockType %>-header">
...
...
cms/templates/js/publish-xblock.underscore
View file @
ef581e11
<%
<%
var publishClass = '';
if (publishState === 'staff_only') {
publishClass = 'is-staff-only';
} else if (publishState === 'live') {
publishClass = 'is-live';
} else if (publishState === 'ready') {
publishClass = 'is-ready';
} else if (publishState === 'has_unpublished_content') {
publishClass = 'has-warnings is-draft';
}
var title = gettext("Draft (Never published)");
var title = gettext("Draft (Never published)");
if (
publish
State === 'staff_only') {
if (
visibility
State === 'staff_only') {
title = gettext("Unpublished (Staff only)");
title = gettext("Unpublished (Staff only)");
} else if (
publish
State === 'live') {
} else if (
visibility
State === 'live') {
title = gettext("Published and Live");
title = gettext("Published and Live");
} else if (
publish
State === 'ready') {
} else if (
visibility
State === 'ready') {
title = gettext("Published");
title = gettext("Published");
} else if (
publishState === 'has_unpublished_content
') {
} else if (
visibilityState === 'needs_attention
') {
title = gettext("Draft (Unpublished changes)");
title = gettext("Draft (Unpublished changes)");
}
}
var releaseLabel = gettext("Release:");
var releaseLabel = gettext("Release:");
if (
publish
State === 'live') {
if (
visibility
State === 'live') {
releaseLabel = gettext("Released:");
releaseLabel = gettext("Released:");
} else if (
publish
State === 'ready') {
} else if (
visibility
State === 'ready') {
releaseLabel = gettext("Scheduled:");
releaseLabel = gettext("Scheduled:");
}
}
var canPublish = publishState !== 'ready' && publishState !== 'live';
var visibleToStaffOnly = visibilityState === 'staff_only';
var canDiscardChanges = publishState === 'has_unpublished_content';
var visibleToStaffOnly = publishState === 'staff_only';
%>
%>
<div class="bit-publishing <%=
publish
Class %>">
<div class="bit-publishing <%=
visibility
Class %>">
<h3 class="bar-mod-title pub-status"><span class="sr"><%= gettext("Publishing Status") %></span>
<h3 class="bar-mod-title pub-status"><span class="sr"><%= gettext("Publishing Status") %></span>
<%= title %>
<%= title %>
</h3>
</h3>
<div class="wrapper-last-draft bar-mod-content">
<div class="wrapper-last-draft bar-mod-content">
<p class="copy meta">
<p class="copy meta">
<% if (
publishState === 'has_unpublished_content'
&& editedOn && editedBy) {
<% if (
hasChanges
&& editedOn && editedBy) {
var message = gettext("Draft saved on %(last_saved_date)s by %(edit_username)s") %>
var message = gettext("Draft saved on %(last_saved_date)s by %(edit_username)s") %>
<%= interpolate(message, {
<%= interpolate(message, {
last_saved_date: '<span class="date">' + editedOn + '</span>',
last_saved_date: '<span class="date">' + editedOn + '</span>',
...
@@ -91,12 +78,12 @@ var visibleToStaffOnly = publishState === 'staff_only';
...
@@ -91,12 +78,12 @@ var visibleToStaffOnly = publishState === 'staff_only';
<div class="wrapper-pub-actions bar-mod-actions">
<div class="wrapper-pub-actions bar-mod-actions">
<ul class="action-list">
<ul class="action-list">
<li class="action-item">
<li class="action-item">
<a class="action-publish action-primary <% if (
!canPublish
) { %>is-disabled<% } %>"
<a class="action-publish action-primary <% if (
published && !hasChanges
) { %>is-disabled<% } %>"
href=""><%= gettext("Publish") %>
href=""><%= gettext("Publish") %>
</a>
</a>
</li>
</li>
<li class="action-item">
<li class="action-item">
<a class="action-discard action-secondary <% if (!
canDiscard
Changes) { %>is-disabled<% } %>"
<a class="action-discard action-secondary <% if (!
published || !has
Changes) { %>is-disabled<% } %>"
href=""><%= gettext("Discard Changes") %>
href=""><%= gettext("Discard Changes") %>
</a>
</a>
</li>
</li>
...
...
cms/templates/js/unit-outline.underscore
View file @
ef581e11
<%
<%
var publishState = xblockInfo.get('publish_state');
var publishClass = '';
if (publishState === 'staff_only') {
publishClass = 'is-staff-only';
} else if (publishState === 'live') {
publishClass = 'is-live';
} else if (publishState === 'ready') {
publishClass = 'is-ready';
} else if (publishState === 'has_unpublished_content') {
publishClass = 'has_warnings';
}
var listType = 'list-for-' + xblockType;
var listType = 'list-for-' + xblockType;
if (xblockType === 'course') {
if (xblockType === 'course') {
listType = 'list-sections';
listType = 'list-sections';
...
@@ -21,7 +9,7 @@ if (xblockType === 'course') {
...
@@ -21,7 +9,7 @@ if (xblockType === 'course') {
}
}
%>
%>
<% if (parentInfo) { %>
<% if (parentInfo) { %>
<li class="outline-item outline-<%= xblockType %> <%=
publish
Class %>"
<li class="outline-item outline-<%= xblockType %> <%=
visibility
Class %>"
data-parent="<%= parentInfo.get('id') %>" data-locator="<%= xblockInfo.get('id') %>">
data-parent="<%= parentInfo.get('id') %>" data-locator="<%= xblockInfo.get('id') %>">
<div class="<%= xblockType %>-header">
<div class="<%= xblockType %>-header">
<h3 class="<%= xblockType %>-header-details">
<h3 class="<%= xblockType %>-header-details">
...
...
cms/templates/widgets/units.html
View file @
ef581e11
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%!
from
contentstore
.
utils
import
compute_publish_state
%
>
<
%!
from
contentstore
.
views
.
helpers
import
xblock_studio_url
%
>
<
%!
from
contentstore
.
views
.
helpers
import
xblock_studio_url
%
>
<!--
<!--
...
@@ -18,7 +17,7 @@ This def will enumerate through a passed in subsection and list all of the units
...
@@ -18,7 +17,7 @@ This def will enumerate through a passed in subsection and list all of the units
<
%
include
file=
"_ui-dnd-indicator-before.html"
/>
<
%
include
file=
"_ui-dnd-indicator-before.html"
/>
<
%
<
%
unit_state =
compute_publish_state(unit)
unit_state =
'draft'
#
Note:
this
is
a
hack
since
this
HTML
file
is
being
removed
in
bulk-publishing
if
unit
.
location =
=
selected:
if
unit
.
location =
=
selected:
selected_class =
'editing'
selected_class =
'editing'
else:
else:
...
...
common/lib/xmodule/xmodule/modulestore/__init__.py
View file @
ef581e11
...
@@ -90,10 +90,10 @@ class ModuleStoreEnum(object):
...
@@ -90,10 +90,10 @@ class ModuleStoreEnum(object):
test
=
-
3
test
=
-
3
class
Legacy
PublishState
(
object
):
class
PublishState
(
object
):
"""
"""
The legacy publish state for a given xblock-- either 'draft', 'private', or 'public'. These states
The legacy publish state for a given xblock-- either 'draft', 'private', or 'public'. These states
are no longer used in Studio
directly, but are still referenced in a few places
.
are no longer used in Studio
, but they are still referenced in a few places in LMS
.
"""
"""
draft
=
'draft'
draft
=
'draft'
private
=
'private'
private
=
'private'
...
@@ -301,10 +301,10 @@ class ModuleStoreRead(object):
...
@@ -301,10 +301,10 @@ class ModuleStoreRead(object):
Returns whether this xblock is draft, public, or private.
Returns whether this xblock is draft, public, or private.
Returns:
Returns:
Legacy
PublishState.draft - content is in the process of being edited, but still has a previous
PublishState.draft - content is in the process of being edited, but still has a previous
version deployed to LMS
version deployed to LMS
Legacy
PublishState.public - content is locked and deployed to LMS
PublishState.public - content is locked and deployed to LMS
Legacy
PublishState.private - content is editable and not deployed to LMS
PublishState.private - content is editable and not deployed to LMS
"""
"""
pass
pass
...
@@ -522,7 +522,7 @@ class ModuleStoreReadBase(ModuleStoreRead):
...
@@ -522,7 +522,7 @@ class ModuleStoreReadBase(ModuleStoreRead):
"""
"""
Returns PublishState.public since this is a read-only store.
Returns PublishState.public since this is a read-only store.
"""
"""
return
Legacy
PublishState
.
public
return
PublishState
.
public
def
heartbeat
(
self
):
def
heartbeat
(
self
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/mixed.py
View file @
ef581e11
...
@@ -439,10 +439,10 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
...
@@ -439,10 +439,10 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
Returns whether this xblock is draft, public, or private.
Returns whether this xblock is draft, public, or private.
Returns:
Returns:
Legacy
PublishState.draft - content is in the process of being edited, but still has a previous
PublishState.draft - content is in the process of being edited, but still has a previous
version deployed to LMS
version deployed to LMS
Legacy
PublishState.public - content is locked and deployed to LMS
PublishState.public - content is locked and deployed to LMS
Legacy
PublishState.private - content is editable and not deployed to LMS
PublishState.private - content is editable and not deployed to LMS
"""
"""
course_id
=
xblock
.
scope_ids
.
usage_id
.
course_key
course_id
=
xblock
.
scope_ids
.
usage_id
.
course_key
store
=
self
.
_get_modulestore_for_courseid
(
course_id
)
store
=
self
.
_get_modulestore_for_courseid
(
course_id
)
...
...
common/lib/xmodule/xmodule/modulestore/mongo/draft.py
View file @
ef581e11
...
@@ -11,7 +11,7 @@ import logging
...
@@ -11,7 +11,7 @@ import logging
from
opaque_keys.edx.locations
import
Location
from
opaque_keys.edx.locations
import
Location
from
xmodule.exceptions
import
InvalidVersionError
from
xmodule.exceptions
import
InvalidVersionError
from
xmodule.modulestore
import
Legacy
PublishState
,
ModuleStoreEnum
from
xmodule.modulestore
import
PublishState
,
ModuleStoreEnum
from
xmodule.modulestore.exceptions
import
(
from
xmodule.modulestore.exceptions
import
(
ItemNotFoundError
,
DuplicateItemError
,
InvalidBranchSetting
,
DuplicateCourseError
ItemNotFoundError
,
DuplicateItemError
,
InvalidBranchSetting
,
DuplicateCourseError
)
)
...
@@ -613,7 +613,7 @@ class DraftModuleStore(MongoModuleStore):
...
@@ -613,7 +613,7 @@ class DraftModuleStore(MongoModuleStore):
return
False
return
False
# don't check children if this block has changes (is not public)
# don't check children if this block has changes (is not public)
if
self
.
compute_publish_state
(
item
)
!=
Legacy
PublishState
.
public
:
if
self
.
compute_publish_state
(
item
)
!=
PublishState
.
public
:
return
True
return
True
# if this block doesn't have changes, then check its children
# if this block doesn't have changes, then check its children
elif
item
.
has_children
:
elif
item
.
has_children
:
...
@@ -792,10 +792,10 @@ class DraftModuleStore(MongoModuleStore):
...
@@ -792,10 +792,10 @@ class DraftModuleStore(MongoModuleStore):
Returns whether this xblock is draft, public, or private.
Returns whether this xblock is draft, public, or private.
Returns:
Returns:
Legacy
PublishState.draft - content is in the process of being edited, but still has a previous
PublishState.draft - content is in the process of being edited, but still has a previous
version deployed to LMS
version deployed to LMS
Legacy
PublishState.public - content is locked and deployed to LMS
PublishState.public - content is locked and deployed to LMS
Legacy
PublishState.private - content is editable and not deployed to LMS
PublishState.private - content is editable and not deployed to LMS
"""
"""
if
getattr
(
xblock
,
'is_draft'
,
False
):
if
getattr
(
xblock
,
'is_draft'
,
False
):
published_xblock_location
=
as_published
(
xblock
.
location
)
published_xblock_location
=
as_published
(
xblock
.
location
)
...
@@ -803,11 +803,11 @@ class DraftModuleStore(MongoModuleStore):
...
@@ -803,11 +803,11 @@ class DraftModuleStore(MongoModuleStore):
{
'_id'
:
published_xblock_location
.
to_deprecated_son
()}
{
'_id'
:
published_xblock_location
.
to_deprecated_son
()}
)
)
if
published_item
is
None
:
if
published_item
is
None
:
return
Legacy
PublishState
.
private
return
PublishState
.
private
else
:
else
:
return
Legacy
PublishState
.
draft
return
PublishState
.
draft
else
:
else
:
return
Legacy
PublishState
.
public
return
PublishState
.
public
def
_verify_branch_setting
(
self
,
expected_branch_setting
):
def
_verify_branch_setting
(
self
,
expected_branch_setting
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py
View file @
ef581e11
...
@@ -4,7 +4,7 @@ Module for the dual-branch fall-back Draft->Published Versioning ModuleStore
...
@@ -4,7 +4,7 @@ Module for the dual-branch fall-back Draft->Published Versioning ModuleStore
from
..exceptions
import
ItemNotFoundError
from
..exceptions
import
ItemNotFoundError
from
split
import
SplitMongoModuleStore
,
EXCLUDE_ALL
from
split
import
SplitMongoModuleStore
,
EXCLUDE_ALL
from
xmodule.modulestore
import
ModuleStoreEnum
,
Legacy
PublishState
from
xmodule.modulestore
import
ModuleStoreEnum
,
PublishState
from
xmodule.modulestore.exceptions
import
InsufficientSpecificationError
from
xmodule.modulestore.exceptions
import
InsufficientSpecificationError
from
xmodule.modulestore.draft_and_published
import
ModuleStoreDraftAndPublished
,
DIRECT_ONLY_CATEGORIES
,
UnsupportedRevisionError
from
xmodule.modulestore.draft_and_published
import
ModuleStoreDraftAndPublished
,
DIRECT_ONLY_CATEGORIES
,
UnsupportedRevisionError
...
@@ -251,9 +251,9 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
...
@@ -251,9 +251,9 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
Returns whether this xblock is draft, public, or private.
Returns whether this xblock is draft, public, or private.
Returns:
Returns:
Legacy
PublishState.draft - published exists and is different from draft
PublishState.draft - published exists and is different from draft
Legacy
PublishState.public - published exists and is the same as draft
PublishState.public - published exists and is the same as draft
Legacy
PublishState.private - no published version exists
PublishState.private - no published version exists
"""
"""
# TODO figure out what to say if xblock is not from the HEAD of its branch
# TODO figure out what to say if xblock is not from the HEAD of its branch
def
get_head
(
branch
):
def
get_head
(
branch
):
...
@@ -272,13 +272,13 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
...
@@ -272,13 +272,13 @@ class DraftVersioningModuleStore(ModuleStoreDraftAndPublished, SplitMongoModuleS
if
not
published_head
:
if
not
published_head
:
# published version does not exist
# published version does not exist
return
Legacy
PublishState
.
private
return
PublishState
.
private
elif
get_version
(
draft_head
)
==
get_version
(
published_head
):
elif
get_version
(
draft_head
)
==
get_version
(
published_head
):
# published and draft versions are equal
# published and draft versions are equal
return
Legacy
PublishState
.
public
return
PublishState
.
public
else
:
else
:
# published and draft versions differ
# published and draft versions differ
return
Legacy
PublishState
.
draft
return
PublishState
.
draft
def
convert_to_draft
(
self
,
location
,
user_id
):
def
convert_to_draft
(
self
,
location
,
user_id
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
View file @
ef581e11
...
@@ -9,7 +9,7 @@ from pytz import UTC
...
@@ -9,7 +9,7 @@ from pytz import UTC
from
xmodule.tests
import
DATA_DIR
from
xmodule.tests
import
DATA_DIR
from
opaque_keys.edx.locations
import
Location
from
opaque_keys.edx.locations
import
Location
from
xmodule.modulestore
import
ModuleStoreEnum
,
Legacy
PublishState
from
xmodule.modulestore
import
ModuleStoreEnum
,
PublishState
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.exceptions
import
InvalidVersionError
from
xmodule.exceptions
import
InvalidVersionError
...
@@ -991,22 +991,22 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -991,22 +991,22 @@ class TestMixedModuleStore(unittest.TestCase):
item_location
=
item
.
location
.
version_agnostic
()
item_location
=
item
.
location
.
version_agnostic
()
mongo_store
=
self
.
store
.
_get_modulestore_for_courseid
(
self
.
_course_key_from_string
(
self
.
MONGO_COURSEID
))
mongo_store
=
self
.
store
.
_get_modulestore_for_courseid
(
self
.
_course_key_from_string
(
self
.
MONGO_COURSEID
))
with
check_mongo_calls
(
mongo_store
,
max_find
,
max_send
):
with
check_mongo_calls
(
mongo_store
,
max_find
,
max_send
):
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
Legacy
PublishState
.
private
)
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
PublishState
.
private
)
# Private -> Public
# Private -> Public
self
.
store
.
publish
(
item_location
,
self
.
user_id
)
self
.
store
.
publish
(
item_location
,
self
.
user_id
)
item
=
self
.
store
.
get_item
(
item_location
)
item
=
self
.
store
.
get_item
(
item_location
)
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
Legacy
PublishState
.
public
)
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
PublishState
.
public
)
# Public -> Private
# Public -> Private
self
.
store
.
unpublish
(
item_location
,
self
.
user_id
)
self
.
store
.
unpublish
(
item_location
,
self
.
user_id
)
item
=
self
.
store
.
get_item
(
item_location
)
item
=
self
.
store
.
get_item
(
item_location
)
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
Legacy
PublishState
.
private
)
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
PublishState
.
private
)
# Private -> Public
# Private -> Public
self
.
store
.
publish
(
item_location
,
self
.
user_id
)
self
.
store
.
publish
(
item_location
,
self
.
user_id
)
item
=
self
.
store
.
get_item
(
item_location
)
item
=
self
.
store
.
get_item
(
item_location
)
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
Legacy
PublishState
.
public
)
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
PublishState
.
public
)
# Public -> Draft with NO changes
# Public -> Draft with NO changes
# Note: This is where Split and Mongo differ
# Note: This is where Split and Mongo differ
...
@@ -1014,14 +1014,14 @@ class TestMixedModuleStore(unittest.TestCase):
...
@@ -1014,14 +1014,14 @@ class TestMixedModuleStore(unittest.TestCase):
item
=
self
.
store
.
get_item
(
item_location
)
item
=
self
.
store
.
get_item
(
item_location
)
self
.
assertEquals
(
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
self
.
store
.
compute_publish_state
(
item
),
LegacyPublishState
.
draft
if
default_ms
==
'draft'
else
Legacy
PublishState
.
public
PublishState
.
draft
if
default_ms
==
'draft'
else
PublishState
.
public
)
)
# Draft WITH changes
# Draft WITH changes
item
.
display_name
=
'new name'
item
.
display_name
=
'new name'
item
=
self
.
store
.
update_item
(
item
,
self
.
user_id
)
item
=
self
.
store
.
update_item
(
item
,
self
.
user_id
)
self
.
assertTrue
(
self
.
store
.
has_changes
(
item
.
location
))
self
.
assertTrue
(
self
.
store
.
has_changes
(
item
.
location
))
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
Legacy
PublishState
.
draft
)
self
.
assertEquals
(
self
.
store
.
compute_publish_state
(
item
),
PublishState
.
draft
)
@ddt.data
(
'draft'
,
'split'
)
@ddt.data
(
'draft'
,
'split'
)
def
test_auto_publish
(
self
,
default_ms
):
def
test_auto_publish
(
self
,
default_ms
):
...
...
common/test/acceptance/tests/base_studio_test.py
View file @
ef581e11
from
..pages.studio.auto_auth
import
AutoAuthPage
from
..pages.studio.auto_auth
import
AutoAuthPage
from
..fixtures.course
import
CourseFixture
from
..fixtures.course
import
CourseFixture
from
acceptance.tests.helpers
import
UniqueCourseTest
from
.helpers
import
UniqueCourseTest
class
StudioCourseTest
(
UniqueCourseTest
):
class
StudioCourseTest
(
UniqueCourseTest
):
...
...
common/test/acceptance/tests/test_studio_general.py
View file @
ef581e11
...
@@ -25,7 +25,7 @@ from ..pages.studio.signup import SignupPage
...
@@ -25,7 +25,7 @@ from ..pages.studio.signup import SignupPage
from
..pages.studio.textbooks
import
TextbooksPage
from
..pages.studio.textbooks
import
TextbooksPage
from
..fixtures.course
import
XBlockFixtureDesc
from
..fixtures.course
import
XBlockFixtureDesc
from
acceptance.tests
.base_studio_test
import
StudioCourseTest
from
.base_studio_test
import
StudioCourseTest
@attr
(
'shard_1'
)
@attr
(
'shard_1'
)
...
...
common/test/acceptance/tests/test_studio_outline.py
View file @
ef581e11
...
@@ -8,7 +8,7 @@ from ..pages.studio.overview import CourseOutlinePage, ContainerPage, ExpandColl
...
@@ -8,7 +8,7 @@ from ..pages.studio.overview import CourseOutlinePage, ContainerPage, ExpandColl
from
..pages.lms.courseware
import
CoursewarePage
from
..pages.lms.courseware
import
CoursewarePage
from
..fixtures.course
import
XBlockFixtureDesc
from
..fixtures.course
import
XBlockFixtureDesc
from
acceptance.tests
.base_studio_test
import
StudioCourseTest
from
.base_studio_test
import
StudioCourseTest
class
CourseOutlineTest
(
StudioCourseTest
):
class
CourseOutlineTest
(
StudioCourseTest
):
...
...
common/test/acceptance/tests/test_studio_split_test.py
View file @
ef581e11
...
@@ -20,7 +20,7 @@ from ..pages.studio.utils import add_advanced_component
...
@@ -20,7 +20,7 @@ from ..pages.studio.utils import add_advanced_component
from
..pages.studio.unit
import
UnitPage
from
..pages.studio.unit
import
UnitPage
from
..pages.xblock.utils
import
wait_for_xblock_initialization
from
..pages.xblock.utils
import
wait_for_xblock_initialization
from
acceptance.tests
.base_studio_test
import
StudioCourseTest
from
.base_studio_test
import
StudioCourseTest
from
test_studio_container
import
ContainerBase
from
test_studio_container
import
ContainerBase
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment