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
c84596a0
Commit
c84596a0
authored
Nov 08, 2012
by
Don Mitchell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Hopefully the course-info changes I had made w/o link destruction
parent
171e9322
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
218 additions
and
7 deletions
+218
-7
cms/djangoapps/contentstore/utils.py
+25
-0
cms/djangoapps/contentstore/views.py
+87
-4
cms/static/coffee/src/views/course_info_edit.coffee
+64
-0
cms/templates/course_info.html
+34
-0
cms/templates/widgets/header.html
+2
-1
cms/urls.py
+6
-2
No files found.
cms/djangoapps/contentstore/utils.py
View file @
c84596a0
...
...
@@ -33,6 +33,31 @@ def get_course_location_for_item(location):
return
location
def
get_course_for_item
(
location
):
'''
cdodge: for a given Xmodule, return the course that it belongs to
NOTE: This makes a lot of assumptions about the format of the course location
Also we have to assert that this module maps to only one course item - it'll throw an
assert if not
'''
item_loc
=
Location
(
location
)
# @hack! We need to find the course location however, we don't
# know the 'name' parameter in this context, so we have
# to assume there's only one item in this query even though we are not specifying a name
course_search_location
=
[
'i4x'
,
item_loc
.
org
,
item_loc
.
course
,
'course'
,
None
]
courses
=
modulestore
()
.
get_items
(
course_search_location
)
# make sure we found exactly one match on this above course search
found_cnt
=
len
(
courses
)
if
found_cnt
==
0
:
raise
BaseException
(
'Could not find course at {0}'
.
format
(
course_search_location
))
if
found_cnt
>
1
:
raise
BaseException
(
'Found more than one course at {0}. There should only be one!!! Dump = {1}'
.
format
(
course_search_location
,
courses
))
return
courses
[
0
]
def
get_lms_link_for_item
(
location
,
preview
=
False
):
location
=
Location
(
location
)
...
...
cms/djangoapps/contentstore/views.py
View file @
c84596a0
...
...
@@ -55,7 +55,7 @@ from cache_toolbox.core import set_cached_content, get_cached_content, del_cache
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
.utils
import
get_course_location_for_item
,
get_lms_link_for_item
,
compute_unit_state
,
get_date_display
,
UnitState
from
.utils
import
get_course_location_for_item
,
get_lms_link_for_item
,
compute_unit_state
,
get_date_display
,
UnitState
,
get_course_for_item
from
xmodule.templates
import
all_templates
from
xmodule.modulestore.xml_importer
import
import_from_xml
...
...
@@ -66,7 +66,10 @@ log = logging.getLogger(__name__)
COMPONENT_TYPES
=
[
'customtag'
,
'discussion'
,
'html'
,
'problem'
,
'video'
]
DIRECT_ONLY_CATEGORIES
=
[
'course'
,
'chapter'
,
'sequential'
]
DIRECT_ONLY_CATEGORIES
=
[
'course'
,
'chapter'
,
'sequential'
,
'about'
,
'static_tab'
,
'course_info'
]
# cdodge: these are categories which should not be parented, they are detached from the hierarchy
DETACHED_CATEGORIES
=
[
'about'
,
'static_tab'
,
'course_info'
]
def
_modulestore
(
location
):
...
...
@@ -619,6 +622,17 @@ def save_item(request):
# commit to datastore
store
.
update_metadata
(
item_location
,
existing_item
.
metadata
)
# cdodge: special case logic for updating static_tabs
# unfortunately tabs are enumerated in the course policy data structure, so if we change the display name of
# the tab, we need to update the course policy (which has a nice .tabs property on it)
item_loc
=
Location
(
item_location
)
if
item_loc
.
category
==
'static_tab'
:
# VS[compat] Rework when we can stop having to support tabs lists in the policy
tag_module
=
store
.
get_item
(
item_location
)
course
=
get_course_for_item
(
item_location
)
course
.
update_tab_reference
(
tag_module
)
modulestore
(
'direct'
)
.
update_metadata
(
course
.
location
,
course
.
metadata
)
return
HttpResponse
()
...
...
@@ -692,7 +706,16 @@ def clone_item(request):
new_item
.
metadata
[
'display_name'
]
=
display_name
_modulestore
(
template
)
.
update_metadata
(
new_item
.
location
.
url
(),
new_item
.
own_metadata
)
_modulestore
(
parent
.
location
)
.
update_children
(
parent_location
,
parent
.
definition
.
get
(
'children'
,
[])
+
[
new_item
.
location
.
url
()])
if
new_item
.
location
.
category
not
in
DETACHED_CATEGORIES
:
_modulestore
(
parent
.
location
)
.
update_children
(
parent_location
,
parent
.
definition
.
get
(
'children'
,
[])
+
[
new_item
.
location
.
url
()])
elif
new_item
.
location
.
category
==
'static_tab'
:
# static tabs - in our data model - are described in the course policy
# VS[compat]: Rework when we can stop having to support tabs lists in the policy
if
parent
.
location
.
category
!=
'course'
:
raise
BaseException
(
'adding a new static_tab must be on a course object'
)
parent
.
add_tab_reference
(
new_item
)
_modulestore
(
parent
.
location
)
.
update_metadata
(
parent
.
location
.
url
(),
parent
.
metadata
)
return
HttpResponse
(
json
.
dumps
({
'id'
:
dest_location
.
url
()}))
...
...
@@ -873,6 +896,25 @@ def edit_static(request, org, course, coursename):
return
render_to_response
(
'edit-static-page.html'
,
{})
def
edit_tabs
(
request
,
org
,
course
,
coursename
):
location
=
[
'i4x'
,
org
,
course
,
'course'
,
coursename
]
course_item
=
modulestore
()
.
get_item
(
location
)
static_tabs_loc
=
Location
(
'i4x'
,
org
,
course
,
'static_tab'
,
None
)
static_tabs
=
modulestore
(
'direct'
)
.
get_items
(
static_tabs_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
})
def
not_found
(
request
):
return
render_to_response
(
'error.html'
,
{
'error'
:
'404'
})
...
...
@@ -883,6 +925,36 @@ def server_error(request):
@login_required
@ensure_csrf_cookie
def
course_info
(
request
,
org
,
course
,
name
):
"""
Display an editable asset library
org, course, name: Attributes of the Location for the item to edit
"""
location
=
[
'i4x'
,
org
,
course
,
'course'
,
name
]
# check that logged in user has permissions to this item
if
not
has_access
(
request
.
user
,
location
):
raise
PermissionDenied
()
course_module
=
modulestore
()
.
get_item
(
location
)
# safe but slower would be to chk that course_info exists and, if not, add it
location
=
[
'i4x'
,
org
,
course
,
'course_info'
,
"updates"
]
# TODO chagne to get_items when we store each update as a separate entry, then no need to force creation
try
:
course_updates
=
modulestore
()
.
get_item
(
location
)
except
ItemNotFoundError
:
template
=
Location
([
'i4x'
,
org
,
"templates"
,
'course_info'
,
"Empty"
])
_modulestore
(
template
)
.
clone_item
(
template
,
location
)
return
render_to_response
(
'course_info.html'
,
{
'active_tab'
:
'courseinfo-tab'
,
'context_course'
:
course_module
,
'updates'
:
course_updates
})
@login_required
@ensure_csrf_cookie
def
asset_index
(
request
,
org
,
course
,
name
):
"""
Display an editable asset library
...
...
@@ -977,10 +1049,21 @@ def create_new_course(request):
# set a default start date to now
new_course
.
metadata
[
'start'
]
=
stringify_time
(
time
.
gmtime
())
# 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
new_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
(
new_course
.
location
.
url
(),
new_course
.
own_metadata
)
create_all_course_groups
(
request
.
user
,
new_course
.
location
)
return
HttpResponse
(
json
.
dumps
({
'id'
:
new_course
.
location
.
url
()}))
@ensure_csrf_cookie
...
...
cms/static/coffee/src/views/course_info_edit.coffee
0 → 100644
View file @
c84596a0
## Derived from and should inherit from a common ancestor w/ ModuleEdit
class
CMS
.
Views
.
CourseInfoEdit
extends
Backbone
.
View
tagName
:
'div'
className
:
'component'
events
:
"click .component-editor .cancel-button"
:
'clickCancelButton'
"click .component-editor .save-button"
:
'clickSaveButton'
"click .component-actions .edit-button"
:
'clickEditButton'
"click .component-actions .delete-button"
:
'onDelete'
initialize
:
->
@
render
()
$component_editor
:
=>
@
$el
.
find
(
'.component-editor'
)
loadDisplay
:
->
XModule
.
loadModule
(
@
$el
.
find
(
'.xmodule_display'
))
loadEdit
:
->
if
not
@
module
@
module
=
XModule
.
loadModule
(
@
$el
.
find
(
'.xmodule_edit'
))
# don't show metadata (deprecated for course_info)
render
:
->
if
@
model
.
id
@
$el
.
load
(
"/preview_component/
#{
@
model
.
id
}
"
,
=>
@
loadDisplay
()
@
delegateEvents
()
)
clickSaveButton
:
(
event
)
=>
event
.
preventDefault
()
data
=
@
module
.
save
()
@
model
.
save
(
data
).
done
(
=>
# # showToastMessage("Your changes have been saved.", null, 3)
@
module
=
null
@
render
()
@
$el
.
removeClass
(
'editing'
)
).
fail
(
->
showToastMessage
(
"There was an error saving your changes. Please try again."
,
null
,
3
)
)
clickCancelButton
:
(
event
)
->
event
.
preventDefault
()
@
$el
.
removeClass
(
'editing'
)
@
$component_editor
().
slideUp
(
150
)
clickEditButton
:
(
event
)
->
event
.
preventDefault
()
@
$el
.
addClass
(
'editing'
)
@
$component_editor
().
slideDown
(
150
)
@
loadEdit
()
onDelete
:
(
event
)
->
# clear contents, don't delete
@
model
.
definition
.
data
=
"<ol></ol>"
# TODO change label to 'clear'
onNew
:
(
event
)
->
ele
=
$
(
@
model
.
definition
.
data
).
find
(
"ol"
)
if
(
ele
)
ele
=
$
(
ele
).
first
().
prepend
(
"<li><h2>"
+
$
.
datepicker
.
formatDate
(
'MM d'
,
new
Date
())
+
"</h2>/n</li>"
);
\ No newline at end of file
cms/templates/course_info.html
0 → 100644
View file @
c84596a0
<
%
inherit
file=
"base.html"
/>
<!-- TODO decode course # from context_course into title -->
<
%
block
name=
"title"
>
Course Info
</
%
block>
<
%
block
name=
"jsextra"
>
<script
type=
"text/javascript"
charset=
"utf-8"
>
$
(
document
).
ready
(
function
(){
editor
=
new
CMS
.
Views
.
CourseInfoEdit
({
el
:
$
(
'.course-updates'
),
model
:
new
CMS
.
Models
.
Module
({
id
:
'${course_updates.location.url()}'
})
});
$
(
".new-update-button"
).
bind
(
'click'
,
editor
.
onNew
);
});
</script>
</
%
block>
<
%
block
name=
"content"
>
<div
class=
"main-wrapper"
>
<div
class=
"inner-wrapper"
>
<h1>
Course Info
</h1>
<div
class=
"main-column"
>
<div
class=
"window"
>
<h2>
Updates
</h2>
<a
href=
"#"
class=
"new-update-button"
>
New Update
</a>
<div
class=
"course-updates"
></div>
<!-- probably replace w/ a vertical where each element of the vertical is a separate update w/ a date and html field -->
</div>
</div>
<div
class=
"sidebar window"
>
</div>
</div>
</div>
</
%
block>
\ No newline at end of file
cms/templates/widgets/header.html
View file @
c84596a0
...
...
@@ -10,7 +10,8 @@
<a
href=
"${reverse('course_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}"
class=
"class-name"
>
${context_course.display_name}
</a>
<ul
class=
"class-nav"
>
<li><a
href=
"${reverse('course_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}"
id=
'courseware-tab'
>
Courseware
</a></li>
<li><a
href=
"${reverse('static_pages', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, coursename=ctx_loc.name))}"
id=
'pages-tab'
style=
"display:none"
>
Pages
</a></li>
<li><a
href=
"${reverse('course_info', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}"
id=
'courseinfo-tab'
>
Course Info
</a></li>
<li><a
href=
"${reverse('edit_tabs', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, coursename=ctx_loc.name))}"
id=
'pages-tab'
>
Tabs
</a></li>
<li><a
href=
"${reverse('asset_index', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}"
id=
'assets-tab'
>
Assets
</a></li>
<li><a
href=
"${reverse('manage_users', kwargs=dict(location=ctx_loc))}"
id=
'users-tab'
>
Users
</a></li>
<li><a
href=
"${reverse('import_course', kwargs=dict(org=ctx_loc.org, course=ctx_loc.course, name=ctx_loc.name))}"
id=
'import-tab'
>
Import
</a></li>
...
...
cms/urls.py
View file @
c84596a0
...
...
@@ -34,8 +34,12 @@ urlpatterns = ('',
'contentstore.views.remove_user'
,
name
=
'remove_user'
),
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)/remove_user$'
,
'contentstore.views.remove_user'
,
name
=
'remove_user'
),
url
(
r'^pages/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$'
,
'contentstore.views.static_pages'
,
name
=
'static_pages'
),
url
(
r'^edit_static/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$'
,
'contentstore.views.edit_static'
,
name
=
'edit_static'
),
url
(
r'^pages/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$'
,
'contentstore.views.static_pages'
,
name
=
'static_pages'
),
url
(
r'^edit_static/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$'
,
'contentstore.views.edit_static'
,
name
=
'edit_static'
),
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/info/(?P<name>[^/]+)$'
,
'contentstore.views.course_info'
,
name
=
'course_info'
),
url
(
r'^edit_tabs/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$'
,
'contentstore.views.edit_tabs'
,
name
=
'edit_tabs'
),
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/assets/(?P<name>[^/]+)$'
,
'contentstore.views.asset_index'
,
name
=
'asset_index'
),
# temporary landing page for a 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