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
9350a2c0
Commit
9350a2c0
authored
May 10, 2013
by
Steve Strassmann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactoring views
parent
0bea50ed
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
924 additions
and
165 deletions
+924
-165
cms/djangoapps/contentstore/views/__init__.py
+12
-16
cms/djangoapps/contentstore/views/access.py
+1
-5
cms/djangoapps/contentstore/views/assets.py
+138
-2
cms/djangoapps/contentstore/views/checklist.py
+8
-1
cms/djangoapps/contentstore/views/component.py
+313
-0
cms/djangoapps/contentstore/views/course.py
+260
-98
cms/djangoapps/contentstore/views/error.py
+1
-1
cms/djangoapps/contentstore/views/item.py
+15
-1
cms/djangoapps/contentstore/views/preview.py
+15
-0
cms/djangoapps/contentstore/views/public.py
+5
-1
cms/djangoapps/contentstore/views/requests.py
+3
-38
cms/djangoapps/contentstore/views/session_kv_store.py
+1
-1
cms/djangoapps/contentstore/views/tabs.py
+110
-0
cms/djangoapps/contentstore/views/user.py
+42
-1
No files found.
cms/djangoapps/contentstore/views/__init__.py
View file @
9350a2c0
from
new
import
*
# TODO: replace asterisks, should explicitly enumerate imports instead
from
error
import
*
from
course
import
*
from
item
import
*
from
public
import
*
from
user
import
*
from
preview
import
*
from
assets
import
*
from
checklist
import
*
from
requests
import
landing
"""
from main import *
from
assets
import
asset_index
,
upload_asset
,
import_course
,
generate_export_course
,
export_course
from
checklist
import
get_checklists
,
update_checklist
"""
from
component
import
*
from
course
import
*
from
error
import
not_found
,
server_error
,
render_404
,
render_500
from
item
import
save_item
,
clone_item
,
delete_item
from
preview
import
preview_dispatch
,
preview_component
from
public
import
signup
,
old_login_redirect
,
login_page
,
howitworks
,
ux_alerts
from
user
import
index
,
add_user
,
remove_user
,
manage_users
from
tabs
import
edit_tabs
,
reorder_static_tabs
from
requests
import
edge
,
event
,
landing
cms/djangoapps/contentstore/views/access.py
View file @
9350a2c0
#from auth.authz import is_user_in_course_group_role, get_users_in_course_group_by_role
#from auth.authz import get_user_by_email, add_user_to_course_group, remove_user_from_course_group
#from auth.authz import INSTRUCTOR_ROLE_NAME, STAFF_ROLE_NAME, create_all_course_groups
from
auth.authz
import
STAFF_ROLE_NAME
,
INSTRUCTOR_ROLE_NAME
from
auth.authz
import
STAFF_ROLE_NAME
,
INSTRUCTOR_ROLE_NAME
from
auth.authz
import
is_user_in_course_group_role
from
auth.authz
import
is_user_in_course_group_role
from
contentstore.utils
import
get_course_location_for_item
from
contentstore.utils
import
get_course_location_for_item
from
django.core.exceptions
import
PermissionDenied
def
get_location_and_verify_access
(
request
,
org
,
course
,
name
):
def
get_location_and_verify_access
(
request
,
org
,
course
,
name
):
"""
"""
...
...
cms/djangoapps/contentstore/views/assets.py
View file @
9350a2c0
import
logging
,
json
,
os
,
tarfile
,
shutil
from
tempfile
import
mkdtemp
from
path
import
path
from
django.conf
import
settings
from
django.http
import
HttpResponse
,
HttpResponseBadRequest
from
django.http
import
HttpResponse
,
HttpResponseBadRequest
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
django_future.csrf
import
ensure_csrf_cookie
from
django_future.csrf
import
ensure_csrf_cookie
from
django.core.urlresolvers
import
reverse
from
django.core.servers.basehttp
import
FileWrapper
from
django.core.files.temp
import
NamedTemporaryFile
from
mitxmako.shortcuts
import
render_to_response
from
cache_toolbox.core
import
del_cached_content
from
contentstore.utils
import
get_url_reverse
from
xmodule.modulestore.xml_importer
import
import_from_xml
from
xmodule.contentstore.django
import
contentstore
from
xmodule.modulestore.xml_exporter
import
export_to_xml
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore
import
Location
from
xmodule.contentstore.content
import
StaticContent
from
xmodule.contentstore.content
import
StaticContent
from
access
import
get_location_and_verify_access
from
xmodule.util.date_utils
import
get_default_time_display
from
xmodule.util.date_utils
import
get_default_time_display
from
mitxmako.shortcuts
import
render_to_response
from
access
import
get_location_and_verify_access
from
auth.authz
import
create_all_course_groups
@login_required
@login_required
@ensure_csrf_cookie
@ensure_csrf_cookie
...
@@ -116,3 +133,122 @@ def upload_asset(request, org, course, coursename):
...
@@ -116,3 +133,122 @@ def upload_asset(request, org, course, coursename):
response
[
'asset_url'
]
=
StaticContent
.
get_url_path_from_location
(
content
.
location
)
response
[
'asset_url'
]
=
StaticContent
.
get_url_path_from_location
(
content
.
location
)
return
response
return
response
@ensure_csrf_cookie
@login_required
def
import_course
(
request
,
org
,
course
,
name
):
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
if
request
.
method
==
'POST'
:
filename
=
request
.
FILES
[
'course-data'
]
.
name
if
not
filename
.
endswith
(
'.tar.gz'
):
return
HttpResponse
(
json
.
dumps
({
'ErrMsg'
:
'We only support uploading a .tar.gz file.'
}))
data_root
=
path
(
settings
.
GITHUB_REPO_ROOT
)
course_subdir
=
"{0}-{1}-{2}"
.
format
(
org
,
course
,
name
)
course_dir
=
data_root
/
course_subdir
if
not
course_dir
.
isdir
():
os
.
mkdir
(
course_dir
)
temp_filepath
=
course_dir
/
filename
logging
.
debug
(
'importing course to {0}'
.
format
(
temp_filepath
))
# stream out the uploaded files in chunks to disk
temp_file
=
open
(
temp_filepath
,
'wb+'
)
for
chunk
in
request
.
FILES
[
'course-data'
]
.
chunks
():
temp_file
.
write
(
chunk
)
temp_file
.
close
()
tf
=
tarfile
.
open
(
temp_filepath
)
tf
.
extractall
(
course_dir
+
'/'
)
# find the 'course.xml' file
for
r
,
d
,
f
in
os
.
walk
(
course_dir
):
for
files
in
f
:
if
files
==
'course.xml'
:
break
if
files
==
'course.xml'
:
break
if
files
!=
'course.xml'
:
return
HttpResponse
(
json
.
dumps
({
'ErrMsg'
:
'Could not find the course.xml file in the package.'
}))
logging
.
debug
(
'found course.xml at {0}'
.
format
(
r
))
if
r
!=
course_dir
:
for
fname
in
os
.
listdir
(
r
):
shutil
.
move
(
r
/
fname
,
course_dir
)
module_store
,
course_items
=
import_from_xml
(
modulestore
(
'direct'
),
settings
.
GITHUB_REPO_ROOT
,
[
course_subdir
],
load_error_modules
=
False
,
static_content_store
=
contentstore
(),
target_location_namespace
=
Location
(
location
),
draft_store
=
modulestore
())
# we can blow this away when we're done importing.
shutil
.
rmtree
(
course_dir
)
logging
.
debug
(
'new course at {0}'
.
format
(
course_items
[
0
]
.
location
))
create_all_course_groups
(
request
.
user
,
course_items
[
0
]
.
location
)
return
HttpResponse
(
json
.
dumps
({
'Status'
:
'OK'
}))
else
:
course_module
=
modulestore
()
.
get_item
(
location
)
return
render_to_response
(
'import.html'
,
{
'context_course'
:
course_module
,
'active_tab'
:
'import'
,
'successful_import_redirect_url'
:
get_url_reverse
(
'CourseOutline'
,
course_module
)
})
@ensure_csrf_cookie
@login_required
def
generate_export_course
(
request
,
org
,
course
,
name
):
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
loc
=
Location
(
location
)
export_file
=
NamedTemporaryFile
(
prefix
=
name
+
'.'
,
suffix
=
".tar.gz"
)
root_dir
=
path
(
mkdtemp
())
# export out to a tempdir
logging
.
debug
(
'root = {0}'
.
format
(
root_dir
))
export_to_xml
(
modulestore
(
'direct'
),
contentstore
(),
loc
,
root_dir
,
name
,
modulestore
())
#filename = root_dir / name + '.tar.gz'
logging
.
debug
(
'tar file being generated at {0}'
.
format
(
export_file
.
name
))
tf
=
tarfile
.
open
(
name
=
export_file
.
name
,
mode
=
'w:gz'
)
tf
.
add
(
root_dir
/
name
,
arcname
=
name
)
tf
.
close
()
# remove temp dir
shutil
.
rmtree
(
root_dir
/
name
)
wrapper
=
FileWrapper
(
export_file
)
response
=
HttpResponse
(
wrapper
,
content_type
=
'application/x-tgz'
)
response
[
'Content-Disposition'
]
=
'attachment; filename=
%
s'
%
os
.
path
.
basename
(
export_file
.
name
)
response
[
'Content-Length'
]
=
os
.
path
.
getsize
(
export_file
.
name
)
return
response
@ensure_csrf_cookie
@login_required
def
export_course
(
request
,
org
,
course
,
name
):
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
course_module
=
modulestore
()
.
get_item
(
location
)
return
render_to_response
(
'export.html'
,
{
'context_course'
:
course_module
,
'active_tab'
:
'export'
,
'successful_import_redirect_url'
:
''
})
cms/djangoapps/contentstore/views/checklist.py
View file @
9350a2c0
import
json
from
django.http
import
HttpResponse
,
HttpResponseBadRequest
from
django.http
import
HttpResponse
,
HttpResponseBadRequest
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
django_future.csrf
import
ensure_csrf_cookie
from
django_future.csrf
import
ensure_csrf_cookie
from
access
import
get_location_and_verify_access
from
mitxmako.shortcuts
import
render_to_response
from
mitxmako.shortcuts
import
render_to_response
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.inheritance
import
own_metadata
from
contentstore.utils
import
get_modulestore
,
get_url_reverse
from
contentstore.utils
import
get_modulestore
,
get_url_reverse
from
requests
import
get_request_method
from
access
import
get_location_and_verify_access
@ensure_csrf_cookie
@ensure_csrf_cookie
@login_required
@login_required
...
...
cms/djangoapps/contentstore/views/component.py
0 → 100644
View file @
9350a2c0
import
json
,
logging
from
collections
import
defaultdict
from
django.http
import
HttpResponse
,
HttpResponseBadRequest
,
HttpResponseForbidden
from
django.contrib.auth.decorators
import
login_required
from
django.core.exceptions
import
PermissionDenied
from
django_future.csrf
import
ensure_csrf_cookie
from
django.conf
import
settings
from
mitxmako.shortcuts
import
render_to_response
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
xmodule.util.date_utils
import
get_default_time_display
from
xblock.core
import
Scope
from
util.json_request
import
expect_json
from
contentstore.module_info_model
import
get_module_info
,
set_module_info
from
contentstore.utils
import
get_modulestore
,
get_lms_link_for_item
,
\
compute_unit_state
,
UnitState
,
get_course_for_item
from
models.settings.course_grading
import
CourseGradingModel
from
requests
import
get_request_method
,
_xmodule_recurse
from
access
import
has_access
,
get_location_and_verify_access
# to install PIL on MacOSX: 'easy_install http://dist.repoze.org/PIL-1.1.6.tar.gz'
log
=
logging
.
getLogger
(
__name__
)
COMPONENT_TYPES
=
[
'customtag'
,
'discussion'
,
'html'
,
'problem'
,
'video'
]
OPEN_ENDED_COMPONENT_TYPES
=
[
"combinedopenended"
,
"peergrading"
]
ADVANCED_COMPONENT_TYPES
=
[
'annotatable'
]
+
OPEN_ENDED_COMPONENT_TYPES
ADVANCED_COMPONENT_CATEGORY
=
'advanced'
ADVANCED_COMPONENT_POLICY_KEY
=
'advanced_modules'
@login_required
def
edit_subsection
(
request
,
location
):
# check that we have permissions to edit this item
course
=
get_course_for_item
(
location
)
if
not
has_access
(
request
.
user
,
course
.
location
):
raise
PermissionDenied
()
item
=
modulestore
()
.
get_item
(
location
,
depth
=
1
)
lms_link
=
get_lms_link_for_item
(
location
,
course_id
=
course
.
location
.
course_id
)
preview_link
=
get_lms_link_for_item
(
location
,
course_id
=
course
.
location
.
course_id
,
preview
=
True
)
# make sure that location references a 'sequential', otherwise return BadRequest
if
item
.
location
.
category
!=
'sequential'
:
return
HttpResponseBadRequest
()
parent_locs
=
modulestore
()
.
get_parent_locations
(
location
,
None
)
# we're for now assuming a single parent
if
len
(
parent_locs
)
!=
1
:
logging
.
error
(
'Multiple (or none) parents have been found for {0}'
.
format
(
location
))
# this should blow up if we don't find any parents, which would be erroneous
parent
=
modulestore
()
.
get_item
(
parent_locs
[
0
])
# remove all metadata from the generic dictionary that is presented in a more normalized UI
policy_metadata
=
dict
(
(
field
.
name
,
field
.
read_from
(
item
))
for
field
in
item
.
fields
if
field
.
name
not
in
[
'display_name'
,
'start'
,
'due'
,
'format'
]
and
field
.
scope
==
Scope
.
settings
)
can_view_live
=
False
subsection_units
=
item
.
get_children
()
for
unit
in
subsection_units
:
state
=
compute_unit_state
(
unit
)
if
state
==
UnitState
.
public
or
state
==
UnitState
.
draft
:
can_view_live
=
True
break
return
render_to_response
(
'edit_subsection.html'
,
{
'subsection'
:
item
,
'context_course'
:
course
,
'create_new_unit_template'
:
Location
(
'i4x'
,
'edx'
,
'templates'
,
'vertical'
,
'Empty'
),
'lms_link'
:
lms_link
,
'preview_link'
:
preview_link
,
'course_graders'
:
json
.
dumps
(
CourseGradingModel
.
fetch
(
course
.
location
)
.
graders
),
'parent_location'
:
course
.
location
,
'parent_item'
:
parent
,
'policy_metadata'
:
policy_metadata
,
'subsection_units'
:
subsection_units
,
'can_view_live'
:
can_view_live
})
@login_required
def
edit_unit
(
request
,
location
):
"""
Display an editing page for the specified module.
Expects a GET request with the parameter 'id'.
id: A Location URL
"""
course
=
get_course_for_item
(
location
)
if
not
has_access
(
request
.
user
,
course
.
location
):
raise
PermissionDenied
()
item
=
modulestore
()
.
get_item
(
location
,
depth
=
1
)
lms_link
=
get_lms_link_for_item
(
item
.
location
,
course_id
=
course
.
location
.
course_id
)
component_templates
=
defaultdict
(
list
)
# Check if there are any advanced modules specified in the course policy. These modules
# should be specified as a list of strings, where the strings are the names of the modules
# in ADVANCED_COMPONENT_TYPES that should be enabled for the course.
course_advanced_keys
=
course
.
advanced_modules
# Set component types according to course policy file
component_types
=
list
(
COMPONENT_TYPES
)
if
isinstance
(
course_advanced_keys
,
list
):
course_advanced_keys
=
[
c
for
c
in
course_advanced_keys
if
c
in
ADVANCED_COMPONENT_TYPES
]
if
len
(
course_advanced_keys
)
>
0
:
component_types
.
append
(
ADVANCED_COMPONENT_CATEGORY
)
else
:
log
.
error
(
"Improper format for course advanced keys! {0}"
.
format
(
course_advanced_keys
))
templates
=
modulestore
()
.
get_items
(
Location
(
'i4x'
,
'edx'
,
'templates'
))
for
template
in
templates
:
category
=
template
.
location
.
category
if
category
in
course_advanced_keys
:
category
=
ADVANCED_COMPONENT_CATEGORY
if
category
in
component_types
:
# This is a hack to create categories for different xmodules
component_templates
[
category
]
.
append
((
template
.
display_name_with_default
,
template
.
location
.
url
(),
hasattr
(
template
,
'markdown'
)
and
template
.
markdown
is
not
None
,
template
.
cms
.
empty
,
))
components
=
[
component
.
location
.
url
()
for
component
in
item
.
get_children
()
]
# TODO (cpennington): If we share units between courses,
# this will need to change to check permissions correctly so as
# to pick the correct parent subsection
containing_subsection_locs
=
modulestore
()
.
get_parent_locations
(
location
,
None
)
containing_subsection
=
modulestore
()
.
get_item
(
containing_subsection_locs
[
0
])
containing_section_locs
=
modulestore
()
.
get_parent_locations
(
containing_subsection
.
location
,
None
)
containing_section
=
modulestore
()
.
get_item
(
containing_section_locs
[
0
])
# cdodge hack. We're having trouble previewing drafts via jump_to redirect
# so let's generate the link url here
# need to figure out where this item is in the list of children as the preview will need this
index
=
1
for
child
in
containing_subsection
.
get_children
():
if
child
.
location
==
item
.
location
:
break
index
=
index
+
1
preview_lms_base
=
settings
.
MITX_FEATURES
.
get
(
'PREVIEW_LMS_BASE'
,
'preview.'
+
settings
.
LMS_BASE
)
preview_lms_link
=
'//{preview_lms_base}/courses/{org}/{course}/{course_name}/courseware/{section}/{subsection}/{index}'
.
format
(
preview_lms_base
=
preview_lms_base
,
lms_base
=
settings
.
LMS_BASE
,
org
=
course
.
location
.
org
,
course
=
course
.
location
.
course
,
course_name
=
course
.
location
.
name
,
section
=
containing_section
.
location
.
name
,
subsection
=
containing_subsection
.
location
.
name
,
index
=
index
)
unit_state
=
compute_unit_state
(
item
)
return
render_to_response
(
'unit.html'
,
{
'context_course'
:
course
,
'active_tab'
:
'courseware'
,
'unit'
:
item
,
'unit_location'
:
location
,
'components'
:
components
,
'component_templates'
:
component_templates
,
'draft_preview_link'
:
preview_lms_link
,
'published_preview_link'
:
lms_link
,
'subsection'
:
containing_subsection
,
'release_date'
:
get_default_time_display
(
containing_subsection
.
lms
.
start
)
if
containing_subsection
.
lms
.
start
is
not
None
else
None
,
'section'
:
containing_section
,
'create_new_unit_template'
:
Location
(
'i4x'
,
'edx'
,
'templates'
,
'vertical'
,
'Empty'
),
'unit_state'
:
unit_state
,
'published_date'
:
item
.
cms
.
published_date
.
strftime
(
'
%
B
%
d,
%
Y'
)
if
item
.
cms
.
published_date
is
not
None
else
None
,
})
@expect_json
@login_required
@ensure_csrf_cookie
def
assignment_type_update
(
request
,
org
,
course
,
category
,
name
):
'''
CRUD operations on assignment types for sections and subsections and anything else gradable.
'''
location
=
Location
([
'i4x'
,
org
,
course
,
category
,
name
])
if
not
has_access
(
request
.
user
,
location
):
raise
HttpResponseForbidden
()
if
request
.
method
==
'GET'
:
return
HttpResponse
(
json
.
dumps
(
CourseGradingModel
.
get_section_grader_type
(
location
)),
mimetype
=
"application/json"
)
elif
request
.
method
==
'POST'
:
# post or put, doesn't matter.
return
HttpResponse
(
json
.
dumps
(
CourseGradingModel
.
update_section_grader_type
(
location
,
request
.
POST
)),
mimetype
=
"application/json"
)
@login_required
@expect_json
def
create_draft
(
request
):
location
=
request
.
POST
[
'id'
]
# check permissions for this user within this course
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
# This clones the existing item location to a draft location (the draft is implicit,
# because modulestore is a Draft modulestore)
modulestore
()
.
clone_item
(
location
,
location
)
return
HttpResponse
()
@login_required
@expect_json
def
publish_draft
(
request
):
location
=
request
.
POST
[
'id'
]
# check permissions for this user within this course
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
item
=
modulestore
()
.
get_item
(
location
)
_xmodule_recurse
(
item
,
lambda
i
:
modulestore
()
.
publish
(
i
.
location
,
request
.
user
.
id
))
return
HttpResponse
()
@login_required
@expect_json
def
unpublish_unit
(
request
):
location
=
request
.
POST
[
'id'
]
# check permissions for this user within this course
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
item
=
modulestore
()
.
get_item
(
location
)
_xmodule_recurse
(
item
,
lambda
i
:
modulestore
()
.
unpublish
(
i
.
location
))
return
HttpResponse
()
@login_required
@ensure_csrf_cookie
def
static_pages
(
request
,
org
,
course
,
coursename
):
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
coursename
)
course
=
modulestore
()
.
get_item
(
location
)
return
render_to_response
(
'static-pages.html'
,
{
'active_tab'
:
'pages'
,
'context_course'
:
course
,
})
def
edit_static
(
request
,
org
,
course
,
coursename
):
return
render_to_response
(
'edit-static-page.html'
,
{})
@expect_json
@login_required
@ensure_csrf_cookie
def
module_info
(
request
,
module_location
):
location
=
Location
(
module_location
)
# check that logged in user has permissions to this item
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
real_method
=
get_request_method
(
request
)
rewrite_static_links
=
request
.
GET
.
get
(
'rewrite_url_links'
,
'True'
)
in
[
'True'
,
'true'
]
logging
.
debug
(
'rewrite_static_links = {0} {1}'
.
format
(
request
.
GET
.
get
(
'rewrite_url_links'
,
'False'
),
rewrite_static_links
))
# check that logged in user has permissions to this item
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
if
real_method
==
'GET'
:
return
HttpResponse
(
json
.
dumps
(
get_module_info
(
get_modulestore
(
location
),
location
,
rewrite_static_links
=
rewrite_static_links
)),
mimetype
=
"application/json"
)
elif
real_method
==
'POST'
or
real_method
==
'PUT'
:
return
HttpResponse
(
json
.
dumps
(
set_module_info
(
get_modulestore
(
location
),
location
,
request
.
POST
)),
mimetype
=
"application/json"
)
else
:
return
HttpResponseBadRequest
()
cms/djangoapps/contentstore/views/course.py
View file @
9350a2c0
import
json
,
time
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
django_future.csrf
import
ensure_csrf_cookie
from
django_future.csrf
import
ensure_csrf_cookie
from
django.conf
import
settings
from
django.core.exceptions
import
PermissionDenied
from
django.http
import
HttpResponse
,
HttpResponseBadRequest
from
django.core.urlresolvers
import
reverse
from
mitxmako.shortcuts
import
render_to_response
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
,
InvalidLocationError
from
xmodule.modulestore
import
Location
from
contentstore.course_info_model
import
get_course_updates
,
update_course_updates
,
delete_course_update
from
contentstore.utils
import
get_lms_link_for_item
,
add_open_ended_panel_tab
,
remove_open_ended_panel_tab
from
models.settings.course_details
import
CourseDetails
,
CourseSettingsEncoder
from
models.settings.course_grading
import
CourseGradingModel
from
models.settings.course_metadata
import
CourseMetadata
from
component
import
OPEN_ENDED_COMPONENT_TYPES
,
ADVANCED_COMPONENT_POLICY_KEY
from
auth.authz
import
create_all_course_groups
from
util.json_request
import
expect_json
from
util.json_request
import
expect_json
from
mitxmako.shortcuts
import
render_to_response
from
access
import
has_access
,
get_location_and_verify_access
from
requests
import
get_request_method
from
tabs
import
initialize_course_tabs
@login_required
@ensure_csrf_cookie
def
course_index
(
request
,
org
,
course
,
name
):
"""
Display an editable course overview.
org, course, name: Attributes of the Location for the item to edit
"""
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
lms_link
=
get_lms_link_for_item
(
location
)
upload_asset_callback_url
=
reverse
(
'upload_asset'
,
kwargs
=
{
'org'
:
org
,
'course'
:
course
,
'coursename'
:
name
})
course
=
modulestore
()
.
get_item
(
location
,
depth
=
3
)
sections
=
course
.
get_children
()
return
render_to_response
(
'overview.html'
,
{
'active_tab'
:
'courseware'
,
'context_course'
:
course
,
'lms_link'
:
lms_link
,
'sections'
:
sections
,
'course_graders'
:
json
.
dumps
(
CourseGradingModel
.
fetch
(
course
.
location
)
.
graders
),
'parent_location'
:
course
.
location
,
'new_section_template'
:
Location
(
'i4x'
,
'edx'
,
'templates'
,
'chapter'
,
'Empty'
),
'new_subsection_template'
:
Location
(
'i4x'
,
'edx'
,
'templates'
,
'sequential'
,
'Empty'
),
# for now they are the same, but the could be different at some point...
'upload_asset_callback_url'
:
upload_asset_callback_url
,
'create_new_unit_template'
:
Location
(
'i4x'
,
'edx'
,
'templates'
,
'vertical'
,
'Empty'
)
})
@login_required
@login_required
@expect_json
@expect_json
...
@@ -63,140 +119,246 @@ def create_new_course(request):
...
@@ -63,140 +119,246 @@ def create_new_course(request):
return
HttpResponse
(
json
.
dumps
({
'id'
:
new_course
.
location
.
url
()}))
return
HttpResponse
(
json
.
dumps
({
'id'
:
new_course
.
location
.
url
()}))
def
initialize_course_tabs
(
course
):
# set up the default tabs
# I've added this because when we add static tabs, the LMS either expects a None for the tabs list or
# at least a list populated with the minimal times
# @TODO: I don't like the fact that the presentation tier is away of these data related constraints, let's find a better
# place for this. Also rather than using a simple list of dictionaries a nice class model would be helpful here
# This logic is repeated in xmodule/modulestore/tests/factories.py
# so if you change anything here, you need to also change it there.
course
.
tabs
=
[{
"type"
:
"courseware"
},
{
"type"
:
"course_info"
,
"name"
:
"Course Info"
},
{
"type"
:
"discussion"
,
"name"
:
"Discussion"
},
{
"type"
:
"wiki"
,
"name"
:
"Wiki"
},
{
"type"
:
"progress"
,
"name"
:
"Progress"
}]
modulestore
(
'direct'
)
.
update_metadata
(
course
.
location
.
url
(),
own_metadata
(
course
))
@ensure_csrf_cookie
@login_required
@login_required
def
import_course
(
request
,
org
,
course
,
name
):
@ensure_csrf_cookie
def
course_info
(
request
,
org
,
course
,
name
,
provided_id
=
None
):
"""
Send models and views as well as html for editing the course info to the client.
org, course, name: Attributes of the Location for the item to edit
"""
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
if
request
.
method
==
'POST'
:
course_module
=
modulestore
()
.
get_item
(
location
)
filename
=
request
.
FILES
[
'course-data'
]
.
name
# get current updates
location
=
[
'i4x'
,
org
,
course
,
'course_info'
,
"updates"
]
if
not
filename
.
endswith
(
'.tar.gz'
):
return
render_to_response
(
'course_info.html'
,
{
return
HttpResponse
(
json
.
dumps
({
'ErrMsg'
:
'We only support uploading a .tar.gz file.'
}))
'active_tab'
:
'courseinfo-tab'
,
'context_course'
:
course_module
,
'url_base'
:
"/"
+
org
+
"/"
+
course
+
"/"
,
'course_updates'
:
json
.
dumps
(
get_course_updates
(
location
)),
'handouts_location'
:
Location
([
'i4x'
,
org
,
course
,
'course_info'
,
'handouts'
])
.
url
()
})
data_root
=
path
(
settings
.
GITHUB_REPO_ROOT
)
course_subdir
=
"{0}-{1}-{2}"
.
format
(
org
,
course
,
name
)
@expect_json
course_dir
=
data_root
/
course_subdir
@login_required
if
not
course_dir
.
isdir
():
@ensure_csrf_cookie
os
.
mkdir
(
course_dir
)
def
course_info_updates
(
request
,
org
,
course
,
provided_id
=
None
):
"""
restful CRUD operations on course_info updates.
org, course: Attributes of the Location for the item to edit
provided_id should be none if it's new (create) and a composite of the update db id + index otherwise.
"""
# ??? No way to check for access permission afaik
# get current updates
location
=
[
'i4x'
,
org
,
course
,
'course_info'
,
"updates"
]
# Hmmm, provided_id is coming as empty string on create whereas I believe it used to be None :-(
# Possibly due to my removing the seemingly redundant pattern in urls.py
if
provided_id
==
''
:
provided_id
=
None
# check that logged in user has permissions to this item
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
temp_filepath
=
course_dir
/
filename
real_method
=
get_request_method
(
request
)
if
request
.
method
==
'GET'
:
return
HttpResponse
(
json
.
dumps
(
get_course_updates
(
location
)),
mimetype
=
"application/json"
)
elif
real_method
==
'DELETE'
:
try
:
return
HttpResponse
(
json
.
dumps
(
delete_course_update
(
location
,
request
.
POST
,
provided_id
)),
mimetype
=
"application/json"
)
except
:
return
HttpResponseBadRequest
(
"Failed to delete"
,
content_type
=
"text/plain"
)
elif
request
.
method
==
'POST'
:
try
:
return
HttpResponse
(
json
.
dumps
(
update_course_updates
(
location
,
request
.
POST
,
provided_id
)),
mimetype
=
"application/json"
)
except
:
return
HttpResponseBadRequest
(
"Failed to save"
,
content_type
=
"text/plain"
)
logging
.
debug
(
'importing course to {0}'
.
format
(
temp_filepath
))
@login_required
@ensure_csrf_cookie
def
get_course_settings
(
request
,
org
,
course
,
name
):
"""
Send models and views as well as html for editing the course settings to the client.
# stream out the uploaded files in chunks to disk
org, course, name: Attributes of the Location for the item to edit
temp_file
=
open
(
temp_filepath
,
'wb+'
)
"""
for
chunk
in
request
.
FILES
[
'course-data'
]
.
chunks
():
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
temp_file
.
write
(
chunk
)
temp_file
.
close
()
tf
=
tarfile
.
open
(
temp_filepath
)
course_module
=
modulestore
()
.
get_item
(
location
)
tf
.
extractall
(
course_dir
+
'/'
)
# find the 'course.xml' file
return
render_to_response
(
'settings.html'
,
{
'context_course'
:
course_module
,
'course_location'
:
location
,
'details_url'
:
reverse
(
course_settings_updates
,
kwargs
=
{
"org"
:
org
,
"course"
:
course
,
"name"
:
name
,
"section"
:
"details"
})
})
for
r
,
d
,
f
in
os
.
walk
(
course_dir
):
for
files
in
f
:
if
files
==
'course.xml'
:
break
if
files
==
'course.xml'
:
break
if
files
!=
'course.xml'
:
@login_required
return
HttpResponse
(
json
.
dumps
({
'ErrMsg'
:
'Could not find the course.xml file in the package.'
}))
@ensure_csrf_cookie
def
course_config_graders_page
(
request
,
org
,
course
,
name
):
"""
Send models and views as well as html for editing the course settings to the client.
logging
.
debug
(
'found course.xml at {0}'
.
format
(
r
))
org, course, name: Attributes of the Location for the item to edit
"""
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
if
r
!=
course_dir
:
course_module
=
modulestore
()
.
get_item
(
location
)
for
fname
in
os
.
listdir
(
r
):
course_details
=
CourseGradingModel
.
fetch
(
location
)
shutil
.
move
(
r
/
fname
,
course_dir
)
module_store
,
course_items
=
import_from_xml
(
modulestore
(
'direct'
),
settings
.
GITHUB_REPO_ROOT
,
return
render_to_response
(
'settings_graders.html'
,
{
[
course_subdir
],
load_error_modules
=
Fals
e
,
'context_course'
:
course_modul
e
,
static_content_store
=
contentstore
()
,
'course_location'
:
location
,
target_location_namespace
=
Location
(
location
),
'course_details'
:
json
.
dumps
(
course_details
,
cls
=
CourseSettingsEncoder
)
draft_store
=
modulestore
()
)
}
)
# we can blow this away when we're done importing.
shutil
.
rmtree
(
course_dir
)
logging
.
debug
(
'new course at {0}'
.
format
(
course_items
[
0
]
.
location
))
@login_required
@ensure_csrf_cookie
def
course_config_advanced_page
(
request
,
org
,
course
,
name
):
"""
Send models and views as well as html for editing the advanced course settings to the client.
create_all_course_groups
(
request
.
user
,
course_items
[
0
]
.
location
)
org, course, name: Attributes of the Location for the item to edit
"""
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
return
HttpResponse
(
json
.
dumps
({
'Status'
:
'OK'
}))
course_module
=
modulestore
()
.
get_item
(
location
)
else
:
course_module
=
modulestore
()
.
get_item
(
location
)
return
render_to_response
(
'import
.html'
,
{
return
render_to_response
(
'settings_advanced
.html'
,
{
'context_course'
:
course_module
,
'context_course'
:
course_module
,
'active_tab'
:
'import'
,
'course_location'
:
location
,
'successful_import_redirect_url'
:
get_url_reverse
(
'CourseOutline'
,
course_module
)
'advanced_dict'
:
json
.
dumps
(
CourseMetadata
.
fetch
(
location
)),
})
})
@e
nsure_csrf_cookie
@e
xpect_json
@login_required
@login_required
def
generate_export_course
(
request
,
org
,
course
,
name
):
@ensure_csrf_cookie
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
def
course_settings_updates
(
request
,
org
,
course
,
name
,
section
):
"""
restful CRUD operations on course settings. This differs from get_course_settings by communicating purely
through json (not rendering any html) and handles section level operations rather than whole page.
org, course: Attributes of the Location for the item to edit
section: one of details, faculty, grading, problems, discussions
"""
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
if
section
==
'details'
:
manager
=
CourseDetails
elif
section
==
'grading'
:
manager
=
CourseGradingModel
else
:
return
loc
=
Location
(
location
)
if
request
.
method
==
'GET'
:
export_file
=
NamedTemporaryFile
(
prefix
=
name
+
'.'
,
suffix
=
".tar.gz"
)
# Cannot just do a get w/o knowing the course name :-(
return
HttpResponse
(
json
.
dumps
(
manager
.
fetch
(
Location
([
'i4x'
,
org
,
course
,
'course'
,
name
])),
cls
=
CourseSettingsEncoder
),
mimetype
=
"application/json"
)
elif
request
.
method
==
'POST'
:
# post or put, doesn't matter.
return
HttpResponse
(
json
.
dumps
(
manager
.
update_from_json
(
request
.
POST
),
cls
=
CourseSettingsEncoder
),
mimetype
=
"application/json"
)
root_dir
=
path
(
mkdtemp
())
# export out to a tempdir
@expect_json
@login_required
@ensure_csrf_cookie
def
course_grader_updates
(
request
,
org
,
course
,
name
,
grader_index
=
None
):
"""
restful CRUD operations on course_info updates. This differs from get_course_settings by communicating purely
through json (not rendering any html) and handles section level operations rather than whole page.
logging
.
debug
(
'root = {0}'
.
format
(
root_dir
))
org, course: Attributes of the Location for the item to edit
"""
export_to_xml
(
modulestore
(
'direct'
),
contentstore
(),
loc
,
root_dir
,
name
,
modulestore
())
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
#filename = root_dir / name + '.tar.gz'
logging
.
debug
(
'tar file being generated at {0}'
.
format
(
export_file
.
name
))
real_method
=
get_request_method
(
request
)
tf
=
tarfile
.
open
(
name
=
export_file
.
name
,
mode
=
'w:gz'
)
tf
.
add
(
root_dir
/
name
,
arcname
=
name
)
tf
.
close
()
# remove temp dir
if
real_method
==
'GET'
:
shutil
.
rmtree
(
root_dir
/
name
)
# Cannot just do a get w/o knowing the course name :-(
return
HttpResponse
(
json
.
dumps
(
CourseGradingModel
.
fetch_grader
(
Location
(
location
),
grader_index
)),
mimetype
=
"application/json"
)
elif
real_method
==
"DELETE"
:
# ??? Should this return anything? Perhaps success fail?
CourseGradingModel
.
delete_grader
(
Location
(
location
),
grader_index
)
return
HttpResponse
()
elif
request
.
method
==
'POST'
:
# post or put, doesn't matter.
return
HttpResponse
(
json
.
dumps
(
CourseGradingModel
.
update_grader_from_json
(
Location
(
location
),
request
.
POST
)),
mimetype
=
"application/json"
)
wrapper
=
FileWrapper
(
export_file
)
response
=
HttpResponse
(
wrapper
,
content_type
=
'application/x-tgz'
)
response
[
'Content-Disposition'
]
=
'attachment; filename=
%
s'
%
os
.
path
.
basename
(
export_file
.
name
)
response
[
'Content-Length'
]
=
os
.
path
.
getsize
(
export_file
.
name
)
return
response
@ensure_csrf_cookie
# # NB: expect_json failed on ["key", "key2"] and json payload
@login_required
@login_required
def
export_course
(
request
,
org
,
course
,
name
):
@ensure_csrf_cookie
def
course_advanced_updates
(
request
,
org
,
course
,
name
):
"""
restful CRUD operations on metadata. The payload is a json rep of the metadata dicts. For delete, otoh,
the payload is either a key or a list of keys to delete.
org, course: Attributes of the Location for the item to edit
"""
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
location
=
get_location_and_verify_access
(
request
,
org
,
course
,
name
)
course_module
=
modulestore
()
.
get_item
(
location
)
real_method
=
get_request_method
(
request
)
if
real_method
==
'GET'
:
return
HttpResponse
(
json
.
dumps
(
CourseMetadata
.
fetch
(
location
)),
mimetype
=
"application/json"
)
elif
real_method
==
'DELETE'
:
return
HttpResponse
(
json
.
dumps
(
CourseMetadata
.
delete_key
(
location
,
json
.
loads
(
request
.
body
))),
mimetype
=
"application/json"
)
elif
real_method
==
'POST'
or
real_method
==
'PUT'
:
# NOTE: request.POST is messed up because expect_json cloned_request.POST.copy() is creating a defective entry w/ the whole payload as the key
request_body
=
json
.
loads
(
request
.
body
)
#Whether or not to filter the tabs key out of the settings metadata
filter_tabs
=
True
#Check to see if the user instantiated any advanced components. This is a hack to add the open ended panel tab
#to a course automatically if the user has indicated that they want to edit the combinedopenended or peergrading
#module, and to remove it if they have removed the open ended elements.
if
ADVANCED_COMPONENT_POLICY_KEY
in
request_body
:
#Check to see if the user instantiated any open ended components
found_oe_type
=
False
#Get the course so that we can scrape current tabs
course_module
=
modulestore
()
.
get_item
(
location
)
for
oe_type
in
OPEN_ENDED_COMPONENT_TYPES
:
if
oe_type
in
request_body
[
ADVANCED_COMPONENT_POLICY_KEY
]:
#Add an open ended tab to the course if needed
changed
,
new_tabs
=
add_open_ended_panel_tab
(
course_module
)
#If a tab has been added to the course, then send the metadata along to CourseMetadata.update_from_json
if
changed
:
request_body
.
update
({
'tabs'
:
new_tabs
})
#Indicate that tabs should not be filtered out of the metadata
filter_tabs
=
False
#Set this flag to avoid the open ended tab removal code below.
found_oe_type
=
True
break
#If we did not find an open ended module type in the advanced settings,
# we may need to remove the open ended tab from the course.
if
not
found_oe_type
:
#Remove open ended tab to the course if needed
changed
,
new_tabs
=
remove_open_ended_panel_tab
(
course_module
)
if
changed
:
request_body
.
update
({
'tabs'
:
new_tabs
})
#Indicate that tabs should not be filtered out of the metadata
filter_tabs
=
False
response_json
=
json
.
dumps
(
CourseMetadata
.
update_from_json
(
location
,
request_body
,
filter_tabs
=
filter_tabs
))
return
HttpResponse
(
response_json
,
mimetype
=
"application/json"
)
return
render_to_response
(
'export.html'
,
{
'context_course'
:
course_module
,
'active_tab'
:
'export'
,
'successful_import_redirect_url'
:
''
})
cms/djangoapps/contentstore/views/error.py
View file @
9350a2c0
from
django.http
import
HttpResponse
,
HttpResponse
ServerError
,
HttpResponseNotFound
from
django.http
import
HttpResponseServerError
,
HttpResponseNotFound
from
mitxmako.shortcuts
import
render_to_string
,
render_to_response
from
mitxmako.shortcuts
import
render_to_string
,
render_to_response
...
...
cms/djangoapps/contentstore/views/item.py
View file @
9350a2c0
import
json
from
uuid
import
uuid4
from
django.core.exceptions
import
PermissionDenied
from
django.http
import
HttpResponse
from
django.http
import
HttpResponse
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.inheritance
import
own_metadata
from
util.json_request
import
expect_json
from
util.json_request
import
expect_json
from
mitxmako.shortcuts
import
render_to_response
from
contentstore.utils
import
get_modulestore
from
access
import
has_access
from
requests
import
_xmodule_recurse
# cdodge: these are categories which should not be parented, they are detached from the hierarchy
DETACHED_CATEGORIES
=
[
'about'
,
'static_tab'
,
'course_info'
]
@login_required
@login_required
...
...
cms/djangoapps/contentstore/views/preview.py
View file @
9350a2c0
import
logging
,
sys
import
static_replace
from
xmodule_modifiers
import
replace_static_urls
from
xmodule.error_module
import
ErrorDescriptor
from
xmodule.errortracker
import
exc_info_to_str
from
django.core.urlresolvers
import
reverse
from
mitxmako.shortcuts
import
render_to_response
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
xblock.runtime
import
DbModel
from
xblock.runtime
import
DbModel
from
xmodule.x_module
import
ModuleSystem
from
xmodule.x_module
import
ModuleSystem
...
@@ -6,6 +13,14 @@ from xmodule_modifiers import wrap_xmodule
...
@@ -6,6 +13,14 @@ from xmodule_modifiers import wrap_xmodule
from
session_kv_store
import
SessionKeyValueStore
from
session_kv_store
import
SessionKeyValueStore
from
requests
import
render_from_lms
from
requests
import
render_from_lms
from
functools
import
partial
from
functools
import
partial
from
xmodule.modulestore
import
Location
from
access
import
has_access
from
xmodule.modulestore.django
import
modulestore
from
xmodule.exceptions
import
NotFoundError
,
ProcessingError
from
django.http
import
HttpResponse
,
Http404
,
HttpResponseBadRequest
,
HttpResponseForbidden
log
=
logging
.
getLogger
(
__name__
)
@login_required
@login_required
def
preview_dispatch
(
request
,
preview_id
,
location
,
dispatch
=
None
):
def
preview_dispatch
(
request
,
preview_id
,
location
,
dispatch
=
None
):
...
...
cms/djangoapps/contentstore/views/public.py
View file @
9350a2c0
from
external_auth.views
import
ssl_login_shortcut
from
external_auth.views
import
ssl_login_shortcut
from
mitxmako.shortcuts
import
render_to_response
from
mitxmako.shortcuts
import
render_to_response
from
django_future.csrf
import
ensure_csrf_cookie
from
django_future.csrf
import
ensure_csrf_cookie
from
requests
import
index
from
django.core.context_processors
import
csrf
from
django.shortcuts
import
redirect
from
django.conf
import
settings
from
user
import
index
"""
"""
Public views
Public views
...
...
cms/djangoapps/contentstore/views/requests.py
View file @
9350a2c0
from
django.contrib.auth.decorators
import
login_required
import
json
from
django_future.csrf
import
ensure_csrf_cookie
from
mitxmako.shortcuts
import
render_to_response
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
access
import
has_access
from
contentstore.utils
import
get_url_reverse
,
get_lms_link_for_item
from
django.conf
import
settings
@login_required
@ensure_csrf_cookie
def
index
(
request
):
"""
List all courses available to the logged in user
"""
courses
=
modulestore
(
'direct'
)
.
get_items
([
'i4x'
,
None
,
None
,
'course'
,
None
])
# filter out courses that we don't have access too
def
course_filter
(
course
):
return
(
has_access
(
request
.
user
,
course
.
location
)
and
course
.
location
.
course
!=
'templates'
and
course
.
location
.
org
!=
''
and
course
.
location
.
course
!=
''
and
course
.
location
.
name
!=
''
)
courses
=
filter
(
course_filter
,
courses
)
return
render_to_response
(
'index.html'
,
{
'new_course_template'
:
Location
(
'i4x'
,
'edx'
,
'templates'
,
'course'
,
'Empty'
),
'courses'
:
[(
course
.
display_name
,
get_url_reverse
(
'CourseOutline'
,
course
),
get_lms_link_for_item
(
course
.
location
,
course_id
=
course
.
location
.
course_id
))
for
course
in
courses
],
'user'
:
request
.
user
,
'disable_course_creation'
:
settings
.
MITX_FEATURES
.
get
(
'DISABLE_COURSE_CREATION'
,
False
)
and
not
request
.
user
.
is_staff
})
# ==== Views with per-item permissions================================
from
django.http
import
HttpResponse
from
mitxmako.shortcuts
import
render_to_string
,
render_to_response
# points to the temporary course landing page with log in and sign up
# points to the temporary course landing page with log in and sign up
def
landing
(
request
,
org
,
course
,
coursename
):
def
landing
(
request
,
org
,
course
,
coursename
):
...
...
cms/djangoapps/contentstore/views/session_kv_store.py
View file @
9350a2c0
from
xblock.runtime
import
KeyValueStore
from
xblock.runtime
import
KeyValueStore
,
InvalidScopeError
class
SessionKeyValueStore
(
KeyValueStore
):
class
SessionKeyValueStore
(
KeyValueStore
):
def
__init__
(
self
,
request
,
model_data
):
def
__init__
(
self
,
request
,
model_data
):
...
...
cms/djangoapps/contentstore/views/tabs.py
0 → 100644
View file @
9350a2c0
from
access
import
has_access
from
util.json_request
import
expect_json
from
django.http
import
HttpResponse
,
HttpResponseBadRequest
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
contentstore.utils
import
get_course_for_item
def
initialize_course_tabs
(
course
):
# set up the default tabs
# I've added this because when we add static tabs, the LMS either expects a None for the tabs list or
# at least a list populated with the minimal times
# @TODO: I don't like the fact that the presentation tier is away of these data related constraints, let's find a better
# place for this. Also rather than using a simple list of dictionaries a nice class model would be helpful here
# This logic is repeated in xmodule/modulestore/tests/factories.py
# so if you change anything here, you need to also change it there.
course
.
tabs
=
[{
"type"
:
"courseware"
},
{
"type"
:
"course_info"
,
"name"
:
"Course Info"
},
{
"type"
:
"discussion"
,
"name"
:
"Discussion"
},
{
"type"
:
"wiki"
,
"name"
:
"Wiki"
},
{
"type"
:
"progress"
,
"name"
:
"Progress"
}]
modulestore
(
'direct'
)
.
update_metadata
(
course
.
location
.
url
(),
own_metadata
(
course
))
@login_required
@expect_json
def
reorder_static_tabs
(
request
):
tabs
=
request
.
POST
[
'tabs'
]
course
=
get_course_for_item
(
tabs
[
0
])
if
not
has_access
(
request
.
user
,
course
.
location
):
raise
PermissionDenied
()
# get list of existing static tabs in course
# make sure they are the same lengths (i.e. the number of passed in tabs equals the number
# that we know about) otherwise we can drop some!
existing_static_tabs
=
[
t
for
t
in
course
.
tabs
if
t
[
'type'
]
==
'static_tab'
]
if
len
(
existing_static_tabs
)
!=
len
(
tabs
):
return
HttpResponseBadRequest
()
# load all reference tabs, return BadRequest if we can't find any of them
tab_items
=
[]
for
tab
in
tabs
:
item
=
modulestore
(
'direct'
)
.
get_item
(
Location
(
tab
))
if
item
is
None
:
return
HttpResponseBadRequest
()
tab_items
.
append
(
item
)
# now just go through the existing course_tabs and re-order the static tabs
reordered_tabs
=
[]
static_tab_idx
=
0
for
tab
in
course
.
tabs
:
if
tab
[
'type'
]
==
'static_tab'
:
reordered_tabs
.
append
({
'type'
:
'static_tab'
,
'name'
:
tab_items
[
static_tab_idx
]
.
display_name
,
'url_slug'
:
tab_items
[
static_tab_idx
]
.
location
.
name
})
static_tab_idx
+=
1
else
:
reordered_tabs
.
append
(
tab
)
# OK, re-assemble the static tabs in the new order
course
.
tabs
=
reordered_tabs
modulestore
(
'direct'
)
.
update_metadata
(
course
.
location
,
own_metadata
(
course
))
return
HttpResponse
()
@login_required
@ensure_csrf_cookie
def
edit_tabs
(
request
,
org
,
course
,
coursename
):
location
=
[
'i4x'
,
org
,
course
,
'course'
,
coursename
]
course_item
=
modulestore
()
.
get_item
(
location
)
# check that logged in user has permissions to this item
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
# see tabs have been uninitialized (e.g. supporing courses created before tab support in studio)
if
course_item
.
tabs
is
None
or
len
(
course_item
.
tabs
)
==
0
:
initialize_course_tabs
(
course_item
)
# first get all static tabs from the tabs list
# we do this because this is also the order in which items are displayed in the LMS
static_tabs_refs
=
[
t
for
t
in
course_item
.
tabs
if
t
[
'type'
]
==
'static_tab'
]
static_tabs
=
[]
for
static_tab_ref
in
static_tabs_refs
:
static_tab_loc
=
Location
(
location
)
.
_replace
(
category
=
'static_tab'
,
name
=
static_tab_ref
[
'url_slug'
])
static_tabs
.
append
(
modulestore
(
'direct'
)
.
get_item
(
static_tab_loc
))
components
=
[
static_tab
.
location
.
url
()
for
static_tab
in
static_tabs
]
return
render_to_response
(
'edit-tabs.html'
,
{
'active_tab'
:
'pages'
,
'context_course'
:
course_item
,
'components'
:
components
})
cms/djangoapps/contentstore/views/user.py
View file @
9350a2c0
from
django.conf
import
settings
from
django.core.exceptions
import
PermissionDenied
from
django.core.urlresolvers
import
reverse
from
django.contrib.auth.decorators
import
login_required
from
django.contrib.auth.decorators
import
login_required
from
django_future.csrf
import
ensure_csrf_cookie
from
django_future.csrf
import
ensure_csrf_cookie
from
util.json_request
import
expect_json
from
mitxmako.shortcuts
import
render_to_response
from
mitxmako.shortcuts
import
render_to_response
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
contentstore.utils
import
get_url_reverse
,
get_lms_link_for_item
from
access
import
has_access
from
requests
import
create_json_response
from
util.json_request
import
expect_json
from
auth.authz
import
STAFF_ROLE_NAME
,
INSTRUCTOR_ROLE_NAME
,
get_users_in_course_group_by_role
from
auth.authz
import
get_user_by_email
,
add_user_to_course_group
,
remove_user_from_course_group
def
user_author_string
(
user
):
def
user_author_string
(
user
):
'''Get an author string for commits by this user. Format:
'''Get an author string for commits by this user. Format:
first last <email@email.com>.
first last <email@email.com>.
...
@@ -22,6 +36,33 @@ def user_author_string(user):
...
@@ -22,6 +36,33 @@ def user_author_string(user):
@login_required
@login_required
@ensure_csrf_cookie
@ensure_csrf_cookie
def
index
(
request
):
"""
List all courses available to the logged in user
"""
courses
=
modulestore
(
'direct'
)
.
get_items
([
'i4x'
,
None
,
None
,
'course'
,
None
])
# filter out courses that we don't have access too
def
course_filter
(
course
):
return
(
has_access
(
request
.
user
,
course
.
location
)
and
course
.
location
.
course
!=
'templates'
and
course
.
location
.
org
!=
''
and
course
.
location
.
course
!=
''
and
course
.
location
.
name
!=
''
)
courses
=
filter
(
course_filter
,
courses
)
return
render_to_response
(
'index.html'
,
{
'new_course_template'
:
Location
(
'i4x'
,
'edx'
,
'templates'
,
'course'
,
'Empty'
),
'courses'
:
[(
course
.
display_name
,
get_url_reverse
(
'CourseOutline'
,
course
),
get_lms_link_for_item
(
course
.
location
,
course_id
=
course
.
location
.
course_id
))
for
course
in
courses
],
'user'
:
request
.
user
,
'disable_course_creation'
:
settings
.
MITX_FEATURES
.
get
(
'DISABLE_COURSE_CREATION'
,
False
)
and
not
request
.
user
.
is_staff
})
@login_required
@ensure_csrf_cookie
def
manage_users
(
request
,
location
):
def
manage_users
(
request
,
location
):
'''
'''
This view will return all CMS users who are editors for the specified course
This view will return all CMS users who are editors for the specified 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