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
67722f1a
Commit
67722f1a
authored
Oct 01, 2013
by
Nick Parlante
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1147 from edx/nick/tab-edit
Create management command to edit course's tabs
parents
81a2ac41
0ed1ee91
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
175 additions
and
1 deletions
+175
-1
CHANGELOG.rst
+3
-0
cms/djangoapps/contentstore/management/commands/edit_course_tabs.py
+88
-0
cms/djangoapps/contentstore/tests/test_tabs.py
+41
-0
cms/djangoapps/contentstore/views/tabs.py
+43
-1
No files found.
CHANGELOG.rst
View file @
67722f1a
...
...
@@ -9,6 +9,9 @@ LMS: Add PaidCourseRegistration mode, where payment is required before course re
LMS: Add split testing functionality for internal use.
CMS: Add edit_course_tabs management command, providing a primitive
editing capability for a course's list of tabs.
Studio and LMS: add ability to lock assets (cannot be viewed unless registered for class).
LMS: Improved accessibility of parts of forum navigation sidebar.
...
...
cms/djangoapps/contentstore/management/commands/edit_course_tabs.py
0 → 100644
View file @
67722f1a
###
### Script for editing the course's tabs
###
#
# Run it this way:
# ./manage.py cms --settings dev edit_course_tabs --course Stanford/CS99/2013_spring
# Or via rake:
# rake django-admin[edit_course_tabs,cms,dev,"--course Stanford/CS99/2013_spring --delete 4"]
#
from
optparse
import
make_option
from
django.core.management.base
import
BaseCommand
,
CommandError
from
.prompt
import
query_yes_no
from
courseware.courses
import
get_course_by_id
from
contentstore.views
import
tabs
def
print_course
(
course
):
"Prints out the course id and a numbered list of tabs."
print
course
.
id
for
index
,
item
in
enumerate
(
course
.
tabs
):
print
index
+
1
,
'"'
+
item
.
get
(
'type'
)
+
'"'
,
'"'
+
item
.
get
(
'name'
,
''
)
+
'"'
# course.tabs looks like this
# [{u'type': u'courseware'}, {u'type': u'course_info', u'name': u'Course Info'}, {u'type': u'textbooks'},
# {u'type': u'discussion', u'name': u'Discussion'}, {u'type': u'wiki', u'name': u'Wiki'},
# {u'type': u'progress', u'name': u'Progress'}]
class
Command
(
BaseCommand
):
help
=
"""See and edit a course's tabs list.
Only supports insertion and deletion. Move and
rename etc. can be done with a delete
followed by an insert.
The tabs are numbered starting with 1.
Tabs 1 and 2 cannot be changed, and tabs of type
static_tab cannot be edited (use Studio for those).
"""
# Making these option objects separately, so can refer to their .help below
course_option
=
make_option
(
'--course'
,
action
=
'store'
,
dest
=
'course'
,
default
=
False
,
help
=
'--course <id> required, e.g. Stanford/CS99/2013_spring'
)
delete_option
=
make_option
(
'--delete'
,
action
=
'store_true'
,
dest
=
'delete'
,
default
=
False
,
help
=
'--delete <tab-number>'
)
insert_option
=
make_option
(
'--insert'
,
action
=
'store_true'
,
dest
=
'insert'
,
default
=
False
,
help
=
'--insert <tab-number> <type> <name>, e.g. 2 "course_info" "Course Info"'
)
option_list
=
BaseCommand
.
option_list
+
(
course_option
,
delete_option
,
insert_option
)
def
handle
(
self
,
*
args
,
**
options
):
if
not
options
[
'course'
]:
raise
CommandError
(
Command
.
course_option
.
help
)
course
=
get_course_by_id
(
options
[
'course'
])
print
'Warning: this command directly edits the list of course tabs in mongo.'
print
'Tabs before any changes:'
print_course
(
course
)
try
:
if
options
[
'delete'
]:
if
len
(
args
)
!=
1
:
raise
CommandError
(
Command
.
delete_option
.
help
)
num
=
int
(
args
[
0
])
if
query_yes_no
(
'Deleting tab {0} Confirm?'
.
format
(
num
),
default
=
'no'
):
tabs
.
primitive_delete
(
course
,
num
-
1
)
# -1 for 0-based indexing
elif
options
[
'insert'
]:
if
len
(
args
)
!=
3
:
raise
CommandError
(
Command
.
insert_option
.
help
)
num
=
int
(
args
[
0
])
tab_type
=
args
[
1
]
name
=
args
[
2
]
if
query_yes_no
(
'Inserting tab {0} "{1}" "{2}" Confirm?'
.
format
(
num
,
tab_type
,
name
),
default
=
'no'
):
tabs
.
primitive_insert
(
course
,
num
-
1
,
tab_type
,
name
)
# -1 as above
except
ValueError
as
e
:
# Cute: translate to CommandError so the CLI error prints nicely.
raise
CommandError
(
e
)
cms/djangoapps/contentstore/tests/test_tabs.py
0 → 100644
View file @
67722f1a
""" Tests for tab functions (just primitive). """
from
contentstore.views
import
tabs
from
django.test
import
TestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
courseware.courses
import
get_course_by_id
class
PrimitiveTabEdit
(
TestCase
):
"""Tests for the primitive tab edit data manipulations"""
def
test_delete
(
self
):
"""Test primitive tab deletion."""
course
=
CourseFactory
.
create
(
org
=
'edX'
,
course
=
'999'
)
with
self
.
assertRaises
(
ValueError
):
tabs
.
primitive_delete
(
course
,
0
)
with
self
.
assertRaises
(
ValueError
):
tabs
.
primitive_delete
(
course
,
1
)
with
self
.
assertRaises
(
IndexError
):
tabs
.
primitive_delete
(
course
,
6
)
tabs
.
primitive_delete
(
course
,
2
)
self
.
assertFalse
({
u'type'
:
u'textbooks'
}
in
course
.
tabs
)
# Check that discussion has shifted down
self
.
assertEquals
(
course
.
tabs
[
2
],
{
'type'
:
'discussion'
,
'name'
:
'Discussion'
})
def
test_insert
(
self
):
"""Test primitive tab insertion."""
course
=
CourseFactory
.
create
(
org
=
'edX'
,
course
=
'999'
)
tabs
.
primitive_insert
(
course
,
2
,
'atype'
,
'aname'
)
self
.
assertEquals
(
course
.
tabs
[
2
],
{
'type'
:
'atype'
,
'name'
:
'aname'
})
with
self
.
assertRaises
(
ValueError
):
tabs
.
primitive_insert
(
course
,
0
,
'atype'
,
'aname'
)
with
self
.
assertRaises
(
ValueError
):
tabs
.
primitive_insert
(
course
,
3
,
'static_tab'
,
'aname'
)
def
test_save
(
self
):
"""Test course saving."""
course
=
CourseFactory
.
create
(
org
=
'edX'
,
course
=
'999'
)
tabs
.
primitive_insert
(
course
,
3
,
'atype'
,
'aname'
)
course2
=
get_course_by_id
(
course
.
id
)
self
.
assertEquals
(
course2
.
tabs
[
3
],
{
'type'
:
'atype'
,
'name'
:
'aname'
})
cms/djangoapps/contentstore/views/tabs.py
View file @
67722f1a
...
...
@@ -9,13 +9,14 @@ from django.contrib.auth.decorators import login_required
from
django.core.exceptions
import
PermissionDenied
from
django_future.csrf
import
ensure_csrf_cookie
from
mitxmako.shortcuts
import
render_to_response
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.inheritance
import
own_metadata
from
xmodule.modulestore.django
import
modulestore
from
..utils
import
get_course_for_item
,
get_modulestore
from
.access
import
get_location_and_verify_access
__all__
=
[
'edit_tabs'
,
'reorder_static_tabs'
,
'static_pages'
]
...
...
@@ -84,6 +85,7 @@ def reorder_static_tabs(request):
# MongoKeyValueStore before we update the mongo datastore.
course
.
save
()
modulestore
(
'direct'
)
.
update_metadata
(
course
.
location
,
own_metadata
(
course
))
# TODO: above two lines are used for the primitive-save case. Maybe factor them out?
return
HttpResponse
()
...
...
@@ -136,3 +138,43 @@ def static_pages(request, org, course, coursename):
return
render_to_response
(
'static-pages.html'
,
{
'context_course'
:
course
,
})
# "primitive" tab edit functions driven by the command line.
# These should be replaced/deleted by a more capable GUI someday.
# Note that the command line UI identifies the tabs with 1-based
# indexing, but this implementation code is standard 0-based.
def
validate_args
(
num
,
tab_type
):
"Throws for the disallowed cases."
if
num
<=
1
:
raise
ValueError
(
'Tabs 1 and 2 cannot be edited'
)
if
tab_type
==
'static_tab'
:
raise
ValueError
(
'Tabs of type static_tab cannot be edited here (use Studio)'
)
def
primitive_delete
(
course
,
num
):
"Deletes the given tab number (0 based)."
tabs
=
course
.
tabs
validate_args
(
num
,
tabs
[
num
]
.
get
(
'type'
,
''
))
del
tabs
[
num
]
# Note for future implementations: if you delete a static_tab, then Chris Dodge
# points out that there's other stuff to delete beyond this element.
# This code happens to not delete static_tab so it doesn't come up.
primitive_save
(
course
)
def
primitive_insert
(
course
,
num
,
tab_type
,
name
):
"Inserts a new tab at the given number (0 based)."
validate_args
(
num
,
tab_type
)
new_tab
=
{
u'type'
:
unicode
(
tab_type
),
u'name'
:
unicode
(
name
)}
tabs
=
course
.
tabs
tabs
.
insert
(
num
,
new_tab
)
primitive_save
(
course
)
def
primitive_save
(
course
):
"Saves the course back to modulestore."
# This code copied from reorder_static_tabs above
course
.
save
()
modulestore
(
'direct'
)
.
update_metadata
(
course
.
location
,
own_metadata
(
course
))
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