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
457b4aa6
Commit
457b4aa6
authored
Dec 07, 2012
by
Don Mitchell
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1108 from MITx/feature/cdodge/course-info
Feature/cdodge/course info
parents
7294e978
402e031e
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
206 additions
and
38 deletions
+206
-38
cms/djangoapps/contentstore/module_info_model.py
+83
-0
cms/djangoapps/contentstore/views.py
+28
-1
cms/static/coffee/src/client_templates/course_info_handouts.html
+9
-20
cms/static/js/models/course_info.js
+3
-0
cms/static/js/models/module_info.js
+11
-0
cms/static/js/template_loader.js
+1
-1
cms/static/js/views/course_info_edit.js
+53
-14
cms/static/sass/_course-info.scss
+6
-0
cms/templates/course_info.html
+8
-2
cms/urls.py
+4
-0
No files found.
cms/djangoapps/contentstore/module_info_model.py
0 → 100644
View file @
457b4aa6
import
logging
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
lxml
import
etree
import
re
from
django.http
import
HttpResponseBadRequest
,
Http404
def
get_module_info
(
store
,
location
,
parent_location
=
None
):
try
:
if
location
.
revision
is
None
:
module
=
store
.
get_item
(
location
)
else
:
module
=
store
.
get_item
(
location
)
except
ItemNotFoundError
:
raise
Http404
return
{
'id'
:
module
.
location
.
url
(),
'data'
:
module
.
definition
[
'data'
],
'metadata'
:
module
.
metadata
}
def
set_module_info
(
store
,
location
,
post_data
):
module
=
None
isNew
=
False
try
:
if
location
.
revision
is
None
:
module
=
store
.
get_item
(
location
)
else
:
module
=
store
.
get_item
(
location
)
except
:
pass
if
module
is
None
:
# new module at this location
# presume that we have an 'Empty' template
template_location
=
Location
([
'i4x'
,
'edx'
,
'templates'
,
location
.
category
,
'Empty'
])
module
=
store
.
clone_item
(
template_location
,
location
)
isNew
=
True
logging
.
debug
(
'post = {0}'
.
format
(
post_data
))
if
post_data
.
get
(
'data'
)
is
not
None
:
data
=
post_data
[
'data'
]
logging
.
debug
(
'data = {0}'
.
format
(
data
))
store
.
update_item
(
location
,
data
)
# cdodge: note calling request.POST.get('children') will return None if children is an empty array
# so it lead to a bug whereby the last component to be deleted in the UI was not actually
# deleting the children object from the children collection
if
'children'
in
post_data
and
post_data
[
'children'
]
is
not
None
:
children
=
post_data
[
'children'
]
store
.
update_children
(
location
,
children
)
# cdodge: also commit any metadata which might have been passed along in the
# POST from the client, if it is there
# NOTE, that the postback is not the complete metadata, as there's system metadata which is
# not presented to the end-user for editing. So let's fetch the original and
# 'apply' the submitted metadata, so we don't end up deleting system metadata
if
post_data
.
get
(
'metadata'
)
is
not
None
:
posted_metadata
=
post_data
[
'metadata'
]
# update existing metadata with submitted metadata (which can be partial)
# IMPORTANT NOTE: if the client passed pack 'null' (None) for a piece of metadata that means 'remove it'
for
metadata_key
in
posted_metadata
.
keys
():
# let's strip out any metadata fields from the postback which have been identified as system metadata
# and therefore should not be user-editable, so we should accept them back from the client
if
metadata_key
in
module
.
system_metadata_fields
:
del
posted_metadata
[
metadata_key
]
elif
posted_metadata
[
metadata_key
]
is
None
:
# remove both from passed in collection as well as the collection read in from the modulestore
if
metadata_key
in
module
.
metadata
:
del
module
.
metadata
[
metadata_key
]
del
posted_metadata
[
metadata_key
]
# overlay the new metadata over the modulestore sourced collection to support partial updates
module
.
metadata
.
update
(
posted_metadata
)
# commit to datastore
store
.
update_metadata
(
location
,
module
.
metadata
)
cms/djangoapps/contentstore/views.py
View file @
457b4aa6
...
...
@@ -49,6 +49,7 @@ from contentstore.course_info_model import get_course_updates,\
update_course_updates
,
delete_course_update
from
cache_toolbox.core
import
del_cached_content
from
xmodule.timeparse
import
stringify_time
from
contentstore.module_info_model
import
get_module_info
,
set_module_info
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -927,7 +928,8 @@ def course_info(request, org, course, name, provided_id=None):
'active_tab'
:
'courseinfo-tab'
,
'context_course'
:
course_module
,
'url_base'
:
"/"
+
org
+
"/"
+
course
+
"/"
,
'course_updates'
:
json
.
dumps
(
get_course_updates
(
location
))
'course_updates'
:
json
.
dumps
(
get_course_updates
(
location
)),
'handouts_location'
:
Location
([
'i4x'
,
org
,
course
,
'course_info'
,
'handouts'
])
.
url
()
})
@expect_json
...
...
@@ -959,6 +961,31 @@ def course_info_updates(request, org, course, provided_id=None):
elif
real_method
==
'DELETE'
:
# coming as POST need to pull from Request Header X-HTTP-Method-Override DELETE
return
HttpResponse
(
json
.
dumps
(
delete_course_update
(
location
,
request
.
POST
,
provided_id
)),
mimetype
=
"application/json"
)
@expect_json
@login_required
@ensure_csrf_cookie
def
module_info
(
request
,
module_location
):
location
=
Location
(
module_location
)
# NB: we're setting Backbone.emulateHTTP to true on the client so everything comes as a post!!!
if
request
.
method
==
'POST'
and
'HTTP_X_HTTP_METHOD_OVERRIDE'
in
request
.
META
:
real_method
=
request
.
META
[
'HTTP_X_HTTP_METHOD_OVERRIDE'
]
else
:
real_method
=
request
.
method
# 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
(
_modulestore
(
location
),
location
)),
mimetype
=
"application/json"
)
elif
real_method
==
'POST'
or
real_method
==
'PUT'
:
return
HttpResponse
(
json
.
dumps
(
set_module_info
(
_modulestore
(
location
),
location
,
request
.
POST
)),
mimetype
=
"application/json"
)
else
:
raise
Http400
@login_required
@ensure_csrf_cookie
def
asset_index
(
request
,
org
,
course
,
name
):
...
...
cms/static/coffee/src/client_templates/course_info_handouts.html
View file @
457b4aa6
<a
href=
"#"
class=
"edit-button"
><span
class=
"edit-icon"
></span>
Edit
</a>
<div
class=
"handouts-content"
>
<h2>
Course Handouts
</h2>
<ol
class=
"treeview-handoutsnav"
>
<li>
<a
href=
"/static/content-mit-6002x/handouts/syllabus.a477535058a1.pdf"
>
Syllabus
</a>
</li>
<li>
<a
href=
"/static/content-mit-6002x/handouts/at-a-glance.9674fe7f677e.pdf"
>
6.002x At-A-Glance
</a>
</li>
<li>
<a
href=
"/static/content-mit-6002x/handouts/syllabus.a477535058a1.pdf"
>
Syllabus
</a>
</li>
<li>
<a
href=
"/static/content-mit-6002x/handouts/at-a-glance.9674fe7f677e.pdf"
>
6.002x At-A-Glance
</a>
</li>
<li>
<a
href=
"/static/content-mit-6002x/handouts/syllabus.a477535058a1.pdf"
>
Syllabus
</a>
</li>
</ol>
</div>
<h2>
Course Handouts
</h2>
<
%
if
(
model
.
get
('
data
')
!=
null
)
{
%
>
<div
class=
"handouts-content"
>
<
%=
model
.
get
('
data
')
%
>
</div>
<
%
}
else
{%
>
<p>
You have no handouts defined
</p>
<
%
}
%
>
<form
class=
"edit-handouts-form"
style=
"display: block;"
>
<div
class=
"row"
>
<textarea
class=
"handouts-content-editor text-editor"
></textarea>
...
...
cms/static/js/models/course_info.js
View file @
457b4aa6
...
...
@@ -29,6 +29,8 @@ CMS.Models.CourseUpdateCollection = Backbone.Collection.extend({
model
:
CMS
.
Models
.
CourseUpdate
});
\ No newline at end of file
cms/static/js/models/module_info.js
0 → 100644
View file @
457b4aa6
CMS
.
Models
.
ModuleInfo
=
Backbone
.
Model
.
extend
({
url
:
function
()
{
return
"/module_info/"
+
this
.
id
;},
defaults
:
{
"id"
:
null
,
"data"
:
null
,
"metadata"
:
null
,
"children"
:
null
},
});
\ No newline at end of file
cms/static/js/template_loader.js
View file @
457b4aa6
...
...
@@ -5,7 +5,7 @@
if
(
typeof
window
.
templateLoader
==
'function'
)
return
;
var
templateLoader
=
{
templateVersion
:
"0.0.
4
"
,
templateVersion
:
"0.0.
6
"
,
templates
:
{},
loadRemoteTemplate
:
function
(
templateName
,
filename
,
callback
)
{
if
(
!
this
.
templates
[
templateName
])
{
...
...
cms/static/js/views/course_info_edit.js
View file @
457b4aa6
...
...
@@ -15,8 +15,8 @@ CMS.Views.CourseInfoEdit = Backbone.View.extend({
});
new
CMS
.
Views
.
ClassInfoHandoutsView
({
el
:
this
.
$
(
'#course-handouts-view'
)
// collection: this.model.get('
')
el
:
this
.
$
(
'#course-handouts-view'
)
,
model
:
this
.
model
.
get
(
'handouts
'
)
});
return
this
;
}
...
...
@@ -41,7 +41,7 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
"/static/coffee/src/client_templates/course_info_update.html"
,
function
(
raw_template
)
{
self
.
template
=
_
.
template
(
raw_template
);
self
.
render
();
self
.
render
();
}
);
},
...
...
@@ -68,6 +68,16 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
this
.
collection
.
add
(
newModel
,
{
at
:
0
});
var
$newForm
=
$
(
this
.
template
({
updateModel
:
newModel
}));
var
$textArea
=
$newForm
.
find
(
".new-update-content"
).
first
();
if
(
this
.
$codeMirror
==
null
)
{
this
.
$codeMirror
=
CodeMirror
.
fromTextArea
(
$textArea
.
get
(
0
),
{
mode
:
"text/html"
,
lineNumbers
:
true
,
lineWrapping
:
true
,
});
}
var
updateEle
=
this
.
$el
.
find
(
"#course-update-list"
);
$
(
updateEle
).
prepend
(
$newForm
);
$newForm
.
addClass
(
'editing'
);
...
...
@@ -85,7 +95,7 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
onSave
:
function
(
event
)
{
var
targetModel
=
this
.
eventModel
(
event
);
console
.
log
(
this
.
contentEntry
(
event
).
val
());
targetModel
.
set
({
date
:
this
.
dateEntry
(
event
).
val
(),
content
:
this
.
contentEntry
(
event
).
val
()
});
targetModel
.
set
({
date
:
this
.
dateEntry
(
event
).
val
(),
content
:
this
.
$codeMirror
.
getValue
()
});
// push change to display, hide the editor, submit the change
this
.
closeEditor
(
this
);
targetModel
.
save
();
...
...
@@ -102,7 +112,17 @@ CMS.Views.ClassInfoUpdateView = Backbone.View.extend({
var
self
=
this
;
this
.
$currentPost
=
$
(
event
.
target
).
closest
(
'li'
);
this
.
$currentPost
.
addClass
(
'editing'
);
$
(
this
.
editor
(
event
)).
slideDown
(
150
);
$
(
this
.
editor
(
event
)).
show
();
var
$textArea
=
this
.
$currentPost
.
find
(
".new-update-content"
).
first
();
if
(
this
.
$codeMirror
==
null
)
{
this
.
$codeMirror
=
CodeMirror
.
fromTextArea
(
$textArea
.
get
(
0
),
{
mode
:
"text/html"
,
lineNumbers
:
true
,
lineWrapping
:
true
,
});
}
$modalCover
.
show
();
var
targetModel
=
this
.
eventModel
(
event
);
$modalCover
.
bind
(
'click'
,
function
()
{
...
...
@@ -185,11 +205,17 @@ CMS.Views.ClassInfoHandoutsView = Backbone.View.extend({
initialize
:
function
()
{
var
self
=
this
;
window
.
templateLoader
.
loadRemoteTemplate
(
"course_info_handouts"
,
"/static/coffee/src/client_templates/course_info_handouts.html"
,
function
(
raw_template
)
{
self
.
template
=
_
.
template
(
raw_template
);
self
.
render
();
this
.
model
.
fetch
(
{
complete
:
function
()
{
window
.
templateLoader
.
loadRemoteTemplate
(
"course_info_handouts"
,
"/static/coffee/src/client_templates/course_info_handouts.html"
,
function
(
raw_template
)
{
self
.
template
=
_
.
template
(
raw_template
);
self
.
render
();
}
);
}
}
);
},
...
...
@@ -197,7 +223,12 @@ CMS.Views.ClassInfoHandoutsView = Backbone.View.extend({
render
:
function
()
{
var
updateEle
=
this
.
$el
;
var
self
=
this
;
this
.
$el
.
append
(
$
(
this
.
template
()));
this
.
$el
.
html
(
$
(
this
.
template
(
{
model
:
this
.
model
})
)
);
this
.
$preview
=
this
.
$el
.
find
(
'.handouts-content'
);
this
.
$form
=
this
.
$el
.
find
(
".edit-handouts-form"
);
this
.
$editor
=
this
.
$form
.
find
(
'.handouts-content-editor'
);
...
...
@@ -207,9 +238,16 @@ CMS.Views.ClassInfoHandoutsView = Backbone.View.extend({
},
onEdit
:
function
(
event
)
{
var
self
=
this
;
this
.
$editor
.
val
(
this
.
$preview
.
html
());
this
.
$form
.
show
();
this
.
$preview
.
hide
();
if
(
this
.
$codeMirror
==
null
)
{
this
.
$codeMirror
=
CodeMirror
.
fromTextArea
(
this
.
$editor
.
get
(
0
),
{
mode
:
"text/html"
,
lineNumbers
:
true
,
lineWrapping
:
true
,
});
}
$modalCover
.
show
();
$modalCover
.
bind
(
'click'
,
function
()
{
self
.
closeEditor
(
self
);
...
...
@@ -217,6 +255,9 @@ CMS.Views.ClassInfoHandoutsView = Backbone.View.extend({
},
onSave
:
function
(
event
)
{
this
.
model
.
set
(
'data'
,
this
.
$codeMirror
.
getValue
());
this
.
render
();
this
.
model
.
save
();
this
.
$form
.
hide
();
this
.
closeEditor
(
this
);
},
...
...
@@ -227,8 +268,6 @@ CMS.Views.ClassInfoHandoutsView = Backbone.View.extend({
},
closeEditor
:
function
(
self
)
{
this
.
$preview
.
html
(
this
.
$editor
.
val
());
this
.
$preview
.
show
();
this
.
$form
.
hide
();
$modalCover
.
unbind
(
'click'
);
$modalCover
.
hide
();
...
...
cms/static/sass/_course-info.scss
View file @
457b4aa6
...
...
@@ -21,6 +21,12 @@
border-radius
:
3px
0
0
3px
;
border-right-color
:
$mediumGrey
;
}
.CodeMirror
{
border
:
1px
solid
#3c3c3c
;
background
:
#fff
;
color
:
#3c3c3c
;
}
}
.course-updates
{
...
...
cms/templates/course_info.html
View file @
457b4aa6
...
...
@@ -8,6 +8,7 @@
<
%
block
name=
"jsextra"
>
<script
type=
"text/javascript"
src=
"${static.url('js/template_loader.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/models/course_info.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/models/module_info.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('js/views/course_info_edit.js')}"
></script>
<link
rel=
"stylesheet"
type=
"text/css"
href=
"${static.url('js/vendor/timepicker/jquery.timepicker.css')}"
/>
<script
src=
"${static.url('js/vendor/timepicker/jquery.timepicker.js')}"
></script>
...
...
@@ -19,14 +20,19 @@
var
course_updates
=
new
CMS
.
Models
.
CourseUpdateCollection
();
course_updates
.
reset
(
$
{
course_updates
|
n
});
course_updates
.
urlbase
=
'${url_base}'
;
var
course_handouts
=
new
CMS
.
Models
.
ModuleInfo
({
id
:
'${handouts_location}'
});
course_handouts
.
urlbase
=
'${url_base}'
;
var
editor
=
new
CMS
.
Views
.
CourseInfoEdit
({
el
:
$
(
'.main-wrapper'
),
model
:
new
CMS
.
Models
.
CourseInfo
({
courseId
:
'${context_course.location}'
,
updates
:
course_updates
,
// FIXME add
handouts
handouts
:
null
})
handouts
:
course_
handouts
})
});
editor
.
render
();
});
...
...
cms/urls.py
View file @
457b4aa6
...
...
@@ -44,6 +44,10 @@ urlpatterns = ('',
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'
),
# this is a generic method to return the data/metadata associated with a xmodule
url
(
r'^module_info/(?P<module_location>.*)$'
,
'contentstore.views.module_info'
,
name
=
'module_info'
),
# temporary landing page for a course
url
(
r'^edge/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$'
,
'contentstore.views.landing'
,
name
=
'landing'
),
...
...
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