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
01e15c1e
Commit
01e15c1e
authored
Apr 23, 2013
by
John Jarvis
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into drupal-new
parents
0c1fd783
7e35bf19
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
35 changed files
with
421 additions
and
116 deletions
+421
-116
cms/djangoapps/contentstore/tests/test_contentstore.py
+0
-0
cms/djangoapps/contentstore/utils.py
+1
-0
cms/djangoapps/contentstore/views.py
+4
-3
cms/one_time_startup.py
+4
-1
cms/static/js/base.js
+0
-1
cms/static/sass/elements/_controls.scss
+1
-1
cms/static/sass/elements/_forms.scss
+72
-8
cms/static/sass/views/_account.scss
+7
-7
cms/static/sass/views/_settings.scss
+80
-26
cms/templates/settings.html
+0
-0
cms/templates/widgets/metadata-only-edit.html
+1
-0
cms/templates/widgets/source-edit.html
+1
-1
common/lib/xmodule/xmodule/capa_module.py
+1
-0
common/lib/xmodule/xmodule/discussion_module.py
+2
-1
common/lib/xmodule/xmodule/editing_module.py
+12
-0
common/lib/xmodule/xmodule/html_module.py
+4
-3
common/lib/xmodule/xmodule/js/src/raw/edit/metadata-only.coffee
+5
-0
common/lib/xmodule/xmodule/modulestore/__init__.py
+1
-2
common/lib/xmodule/xmodule/modulestore/draft.py
+10
-7
common/lib/xmodule/xmodule/modulestore/mongo.py
+26
-3
common/lib/xmodule/xmodule/modulestore/xml_exporter.py
+19
-2
common/lib/xmodule/xmodule/modulestore/xml_importer.py
+0
-0
common/lib/xmodule/xmodule/raw_module.py
+2
-2
common/lib/xmodule/xmodule/templates/discussion/default.yaml
+2
-2
common/lib/xmodule/xmodule/templates/html/latex_html.yaml
+1
-2
common/lib/xmodule/xmodule/templates/problem/latex_problem.yaml
+1
-2
common/lib/xmodule/xmodule/templates/problem/problem_with_hint.yaml
+0
-1
common/lib/xmodule/xmodule/x_module.py
+3
-1
common/lib/xmodule/xmodule/xml_module.py
+2
-3
lms/djangoapps/courseware/tests/test_login.py
+17
-19
lms/djangoapps/django_comment_client/management/commands/seed_permissions_roles.py
+5
-4
lms/djangoapps/django_comment_client/tests/mock_cs_server/__init__.py
+0
-0
lms/djangoapps/django_comment_client/tests/mock_cs_server/mock_cs_server.py
+76
-0
lms/djangoapps/django_comment_client/tests/mock_cs_server/test_mock_cs_server.py
+59
-0
lms/djangoapps/django_comment_client/utils.py
+2
-14
No files found.
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
01e15c1e
This diff is collapsed.
Click to expand it.
cms/djangoapps/contentstore/utils.py
View file @
01e15c1e
...
@@ -11,6 +11,7 @@ DIRECT_ONLY_CATEGORIES = ['course', 'chapter', 'sequential', 'about', 'static_ta
...
@@ -11,6 +11,7 @@ DIRECT_ONLY_CATEGORIES = ['course', 'chapter', 'sequential', 'about', 'static_ta
#In order to instantiate an open ended tab automatically, need to have this data
#In order to instantiate an open ended tab automatically, need to have this data
OPEN_ENDED_PANEL
=
{
"name"
:
"Open Ended Panel"
,
"type"
:
"open_ended"
}
OPEN_ENDED_PANEL
=
{
"name"
:
"Open Ended Panel"
,
"type"
:
"open_ended"
}
def
get_modulestore
(
location
):
def
get_modulestore
(
location
):
"""
"""
Returns the correct modulestore to use for modifying the specified location
Returns the correct modulestore to use for modifying the specified location
...
...
cms/djangoapps/contentstore/views.py
View file @
01e15c1e
...
@@ -1586,7 +1586,8 @@ def import_course(request, org, course, name):
...
@@ -1586,7 +1586,8 @@ def import_course(request, org, course, name):
shutil
.
move
(
r
/
fname
,
course_dir
)
shutil
.
move
(
r
/
fname
,
course_dir
)
module_store
,
course_items
=
import_from_xml
(
modulestore
(
'direct'
),
settings
.
GITHUB_REPO_ROOT
,
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
))
[
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.
# we can blow this away when we're done importing.
shutil
.
rmtree
(
course_dir
)
shutil
.
rmtree
(
course_dir
)
...
@@ -1620,8 +1621,8 @@ def generate_export_course(request, org, course, name):
...
@@ -1620,8 +1621,8 @@ def generate_export_course(request, org, course, name):
logging
.
debug
(
'root = {0}'
.
format
(
root_dir
))
logging
.
debug
(
'root = {0}'
.
format
(
root_dir
))
export_to_xml
(
modulestore
(
'direct'
),
contentstore
(),
loc
,
root_dir
,
name
)
export_to_xml
(
modulestore
(
'direct'
),
contentstore
(),
loc
,
root_dir
,
name
,
modulestore
()
)
#
filename = root_dir / name + '.tar.gz'
#filename = root_dir / name + '.tar.gz'
logging
.
debug
(
'tar file being generated at {0}'
.
format
(
export_file
.
name
))
logging
.
debug
(
'tar file being generated at {0}'
.
format
(
export_file
.
name
))
tf
=
tarfile
.
open
(
name
=
export_file
.
name
,
mode
=
'w:gz'
)
tf
=
tarfile
.
open
(
name
=
export_file
.
name
,
mode
=
'w:gz'
)
...
...
cms/one_time_startup.py
View file @
01e15c1e
from
dogapi
import
dog_http_api
,
dog_stats_api
from
dogapi
import
dog_http_api
,
dog_stats_api
from
django.conf
import
settings
from
django.conf
import
settings
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
django.dispatch
import
Signal
from
request_cache.middleware
import
RequestCache
from
request_cache.middleware
import
RequestCache
from
django.core.cache
import
get_cache
,
InvalidCacheBackendError
from
django.core.cache
import
get_cache
cache
=
get_cache
(
'mongo_metadata_inheritance'
)
cache
=
get_cache
(
'mongo_metadata_inheritance'
)
for
store_name
in
settings
.
MODULESTORE
:
for
store_name
in
settings
.
MODULESTORE
:
...
@@ -11,6 +12,8 @@ for store_name in settings.MODULESTORE:
...
@@ -11,6 +12,8 @@ for store_name in settings.MODULESTORE:
store
.
metadata_inheritance_cache_subsystem
=
cache
store
.
metadata_inheritance_cache_subsystem
=
cache
store
.
request_cache
=
RequestCache
.
get_request_cache
()
store
.
request_cache
=
RequestCache
.
get_request_cache
()
modulestore_update_signal
=
Signal
(
providing_args
=
[
'modulestore'
,
'course_id'
,
'location'
])
store
.
modulestore_update_signal
=
modulestore_update_signal
if
hasattr
(
settings
,
'DATADOG_API'
):
if
hasattr
(
settings
,
'DATADOG_API'
):
dog_http_api
.
api_key
=
settings
.
DATADOG_API
dog_http_api
.
api_key
=
settings
.
DATADOG_API
dog_stats_api
.
start
(
api_key
=
settings
.
DATADOG_API
,
statsd
=
True
)
dog_stats_api
.
start
(
api_key
=
settings
.
DATADOG_API
,
statsd
=
True
)
cms/static/js/base.js
View file @
01e15c1e
...
@@ -225,7 +225,6 @@ function toggleSections(e) {
...
@@ -225,7 +225,6 @@ function toggleSections(e) {
function
editSectionPublishDate
(
e
)
{
function
editSectionPublishDate
(
e
)
{
e
.
preventDefault
();
e
.
preventDefault
();
$modal
=
$
(
'.edit-subsection-publish-settings'
).
show
();
$modal
=
$
(
'.edit-subsection-publish-settings'
).
show
();
$modal
=
$
(
'.edit-subsection-publish-settings'
).
show
();
$modal
.
attr
(
'data-id'
,
$
(
this
).
attr
(
'data-id'
));
$modal
.
attr
(
'data-id'
,
$
(
this
).
attr
(
'data-id'
));
$modal
.
find
(
'.start-date'
).
val
(
$
(
this
).
attr
(
'data-date'
));
$modal
.
find
(
'.start-date'
).
val
(
$
(
this
).
attr
(
'data-date'
));
$modal
.
find
(
'.start-time'
).
val
(
$
(
this
).
attr
(
'data-time'
));
$modal
.
find
(
'.start-time'
).
val
(
$
(
this
).
attr
(
'data-time'
));
...
...
cms/static/sass/elements/_controls.scss
View file @
01e15c1e
...
@@ -97,7 +97,7 @@
...
@@ -97,7 +97,7 @@
color
:
$blue
;
color
:
$blue
;
&
:hover
,
&
:active
{
&
:hover
,
&
:active
{
background
:
$blue-l
3
;
background
:
$blue-l
4
;
color
:
$blue-s2
;
color
:
$blue-s2
;
}
}
...
...
cms/static/sass/elements/_forms.scss
View file @
01e15c1e
...
@@ -8,11 +8,11 @@ input[type="password"],
...
@@ -8,11 +8,11 @@ input[type="password"],
textarea
.text
{
textarea
.text
{
padding
:
6px
8px
8px
;
padding
:
6px
8px
8px
;
@include
box-sizing
(
border-box
);
@include
box-sizing
(
border-box
);
border
:
1px
solid
$
mediumGrey
;
border
:
1px
solid
$
gray-l2
;
border-radius
:
2px
;
border-radius
:
2px
;
@include
linear-gradient
(
$
lightGrey
,
tint
(
$lightGrey
,
90%
)
);
@include
linear-gradient
(
$
gray-l5
,
$white
);
background-color
:
$
lightGrey
;
background-color
:
$
gray-l5
;
@include
box-shadow
(
0
1px
2px
rgba
(
0
,
0
,
0
,
.1
)
inset
);
@include
box-shadow
(
inset
0
1px
2px
$shadow-l1
);
font-family
:
'Open Sans'
,
sans-serif
;
font-family
:
'Open Sans'
,
sans-serif
;
font-size
:
11px
;
font-size
:
11px
;
color
:
$baseFontColor
;
color
:
$baseFontColor
;
...
@@ -21,7 +21,7 @@ textarea.text {
...
@@ -21,7 +21,7 @@ textarea.text {
&
:
:-
webkit-input-placeholder
,
&
:
:-
webkit-input-placeholder
,
&:-
moz-placeholder
,
&:-
moz-placeholder
,
&:-
ms-input-placeholder
{
&:-
ms-input-placeholder
{
color
:
#979faf
;
color
:
$gray-l2
;
}
}
&
:focus
{
&
:focus
{
...
@@ -30,7 +30,72 @@ textarea.text {
...
@@ -30,7 +30,72 @@ textarea.text {
}
}
}
}
// forms - specific
// ====================
// forms - fields - not editable
.field.is-not-editable
{
&
label
.is-focused
{
color
:
$gray-d2
;
}
label
,
input
,
textarea
{
pointer-events
:
none
;
}
}
// ====================
// field with error
.field.error
{
input
,
textarea
{
border-color
:
$red
;
}
}
// ====================
// forms - additional UI
form
{
.note
{
@include
box-sizing
(
border-box
);
.title
{
}
.copy
{
}
// note with actions
&
.has-actions
{
@include
clearfix
();
.title
{
}
.copy
{
}
.list-actions
{
}
}
}
.note-promotion
{
}
}
// ====================
// forms - grandfathered
input
.search
{
input
.search
{
padding
:
6px
15px
8px
30px
;
padding
:
6px
15px
8px
30px
;
@include
box-sizing
(
border-box
);
@include
box-sizing
(
border-box
);
...
@@ -73,4 +138,4 @@ code {
...
@@ -73,4 +138,4 @@ code {
background-color
:
#edf1f5
;
background-color
:
#edf1f5
;
@include
box-shadow
(
0
1px
2px
rgba
(
0
,
0
,
0
,
0
.1
)
inset
);
@include
box-shadow
(
0
1px
2px
rgba
(
0
,
0
,
0
,
0
.1
)
inset
);
font-family
:
Monaco
,
monospace
;
font-family
:
Monaco
,
monospace
;
}
}
\ No newline at end of file
cms/static/sass/views/_account.scss
View file @
01e15c1e
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
body
.signup
,
body
.signin
{
body
.signup
,
body
.signin
{
.wrapper-content
{
.wrapper-content
{
margin
:
0
;
margin
:
(
$baseline
*
1
.5
)
0
0
0
;
padding
:
0
$baseline
;
padding
:
0
$baseline
;
position
:
relative
;
position
:
relative
;
width
:
100%
;
width
:
100%
;
...
@@ -18,7 +18,7 @@ body.signup, body.signin {
...
@@ -18,7 +18,7 @@ body.signup, body.signin {
width
:
flex-grid
(
12
);
width
:
flex-grid
(
12
);
margin
:
0
auto
;
margin
:
0
auto
;
color
:
$gray-d2
;
color
:
$gray-d2
;
header
{
header
{
position
:
relative
;
position
:
relative
;
margin-bottom
:
$baseline
;
margin-bottom
:
$baseline
;
...
@@ -121,7 +121,7 @@ body.signup, body.signin {
...
@@ -121,7 +121,7 @@ body.signup, body.signin {
@include
font-size
(
16
);
@include
font-size
(
16
);
height
:
100%
;
height
:
100%
;
width
:
100%
;
width
:
100%
;
padding
:
(
$baseline
/
2
);
padding
:
(
$baseline
/
2
);
&
.long
{
&
.long
{
width
:
100%
;
width
:
100%
;
...
@@ -136,15 +136,15 @@ body.signup, body.signin {
...
@@ -136,15 +136,15 @@ body.signup, body.signin {
}
}
:-moz-placeholder
{
:-moz-placeholder
{
color
:
$gray-l3
;
color
:
$gray-l3
;
}
}
::-moz-placeholder
{
::-moz-placeholder
{
color
:
$gray-l3
;
color
:
$gray-l3
;
}
}
:-ms-input-placeholder
{
:-ms-input-placeholder
{
color
:
$gray-l3
;
color
:
$gray-l3
;
}
}
&
:focus
{
&
:focus
{
...
...
cms/static/sass/views/_settings.scss
View file @
01e15c1e
...
@@ -147,7 +147,7 @@ body.course.settings {
...
@@ -147,7 +147,7 @@ body.course.settings {
}
}
label
{
label
{
@
include
font-size
(
14
)
;
@
extend
.t-copy-sub1
;
@include
transition
(
color
,
0
.15s
,
ease-in-out
);
@include
transition
(
color
,
0
.15s
,
ease-in-out
);
margin
:
0
0
(
$baseline
/
4
)
0
;
margin
:
0
0
(
$baseline
/
4
)
0
;
font-weight
:
400
;
font-weight
:
400
;
...
@@ -161,7 +161,7 @@ body.course.settings {
...
@@ -161,7 +161,7 @@ body.course.settings {
@include
placeholder
(
$gray-l4
);
@include
placeholder
(
$gray-l4
);
@include
font-size
(
16
);
@include
font-size
(
16
);
@include
size
(
100%
,
100%
);
@include
size
(
100%
,
100%
);
padding
:
(
$baseline
/
2
);
padding
:
(
$baseline
/
2
);
&
.long
{
&
.long
{
}
}
...
@@ -212,7 +212,7 @@ body.course.settings {
...
@@ -212,7 +212,7 @@ body.course.settings {
padding
:
$baseline
;
padding
:
$baseline
;
&
:last-child
{
&
:last-child
{
padding-bottom
:
$baseline
;
padding-bottom
:
$baseline
;
}
}
.actions
{
.actions
{
...
@@ -238,33 +238,36 @@ body.course.settings {
...
@@ -238,33 +238,36 @@ body.course.settings {
}
}
}
}
// not editable fields
.field.is-not-editable
{
&
label
.is-focused
{
color
:
$gray-d2
;
}
}
// field with error
.field.error
{
input
,
textarea
{
border-color
:
$red
;
}
}
// specific fields - basic
// specific fields - basic
&
.basic
{
&
.basic
{
.list-input
{
.list-input
{
@include
clearfix
();
@include
clearfix
();
padding
:
0
(
$baseline
/
2
);
.field
{
.field
{
margin-bottom
:
0
;
margin-bottom
:
0
;
}
}
}
}
// course details that should appear more like content than elements to change
.field.is-not-editable
{
label
{
}
input
,
textarea
{
@extend
.t-copy-lead1
;
@include
box-shadow
(
none
);
border
:
none
;
background
:
none
;
padding
:
0
;
margin
:
0
;
font-weight
:
600
;
}
}
#field-course-organization
{
#field-course-organization
{
float
:
left
;
float
:
left
;
width
:
flex-grid
(
2
,
9
);
width
:
flex-grid
(
2
,
9
);
...
@@ -281,6 +284,58 @@ body.course.settings {
...
@@ -281,6 +284,58 @@ body.course.settings {
float
:
left
;
float
:
left
;
width
:
flex-grid
(
5
,
9
);
width
:
flex-grid
(
5
,
9
);
}
}
// course link note
.note-promotion-courseURL
{
@include
box-shadow
(
0
2px
1px
$shadow-l1
);
@include
border-radius
((
$baseline
/
5
));
margin-top
:
(
$baseline
*
1
.5
);
border
:
1px
solid
$gray-l2
;
padding
:
(
$baseline
/
2
)
0
0
0
;
.title
{
@extend
.t-copy-sub1
;
margin
:
0
0
(
$baseline
/
10
)
0
;
padding
:
0
(
$baseline
/
2
);
.tip
{
display
:
inline
;
margin-left
:
(
$baseline
/
4
);
}
}
.copy
{
padding
:
0
(
$baseline
/
2
)
(
$baseline
/
2
)
(
$baseline
/
2
);
.link-courseURL
{
@extend
.t-copy-lead1
;
&
:hover
{
}
}
}
.list-actions
{
@include
box-shadow
(
inset
0
1px
1px
$shadow-l1
);
border-top
:
1px
solid
$gray-l2
;
padding
:
(
$baseline
/
2
);
background
:
$gray-l5
;
.action-primary
{
@include
blue-button
();
@include
font-size
(
13
);
font-weight
:
600
;
.icon
{
@extend
.t-icon
;
@include
font-size
(
16
);
display
:
inline-block
;
vertical-align
:
middle
;
}
}
}
}
}
}
// specific fields - schedule
// specific fields - schedule
...
@@ -322,7 +377,7 @@ body.course.settings {
...
@@ -322,7 +377,7 @@ body.course.settings {
}
}
}
}
}
}
// specific fields - overview
// specific fields - overview
#field-course-overview
{
#field-course-overview
{
...
@@ -468,7 +523,7 @@ body.course.settings {
...
@@ -468,7 +523,7 @@ body.course.settings {
}
}
}
}
}
}
.grade-specific-bar
{
.grade-specific-bar
{
height
:
50px
!
important
;
height
:
50px
!
important
;
}
}
...
@@ -479,7 +534,7 @@ body.course.settings {
...
@@ -479,7 +534,7 @@ body.course.settings {
li
{
li
{
position
:
absolute
;
position
:
absolute
;
top
:
0
;
top
:
0
;
height
:
50px
;
height
:
50px
;
text-align
:
right
;
text-align
:
right
;
@include
border-radius
(
2px
);
@include
border-radius
(
2px
);
...
@@ -600,8 +655,8 @@ body.course.settings {
...
@@ -600,8 +655,8 @@ body.course.settings {
}
}
#field-course-grading-assignment-shortname
,
#field-course-grading-assignment-shortname
,
#field-course-grading-assignment-totalassignments
,
#field-course-grading-assignment-totalassignments
,
#field-course-grading-assignment-gradeweight
,
#field-course-grading-assignment-gradeweight
,
#field-course-grading-assignment-droppable
{
#field-course-grading-assignment-droppable
{
width
:
flex-grid
(
2
,
6
);
width
:
flex-grid
(
2
,
6
);
}
}
...
@@ -734,4 +789,4 @@ body.course.settings {
...
@@ -734,4 +789,4 @@ body.course.settings {
.content-supplementary
{
.content-supplementary
{
width
:
flex-grid
(
3
,
12
);
width
:
flex-grid
(
3
,
12
);
}
}
}
}
\ No newline at end of file
cms/templates/settings.html
View file @
01e15c1e
This diff is collapsed.
Click to expand it.
cms/templates/widgets/metadata-only-edit.html
0 → 100644
View file @
01e15c1e
<
%
include
file=
"metadata-edit.html"
/>
cms/templates/widgets/source-edit.html
View file @
01e15c1e
...
@@ -12,7 +12,7 @@
...
@@ -12,7 +12,7 @@
<form
id=
"hls-form"
enctype=
"multipart/form-data"
>
<form
id=
"hls-form"
enctype=
"multipart/form-data"
>
<section
class=
"source-edit"
>
<section
class=
"source-edit"
>
<textarea
name=
""
data-metadata-name=
"source_code"
class=
"source-edit-box hls-data"
rows=
"8"
cols=
"40"
>
${
metadata
['source_code']|h}
</textarea>
<textarea
name=
""
data-metadata-name=
"source_code"
class=
"source-edit-box hls-data"
rows=
"8"
cols=
"40"
>
${
editable_metadata_fields
['source_code']|h}
</textarea>
</section>
</section>
<div
class=
"submit"
>
<div
class=
"submit"
>
<button
type=
"reset"
class=
"hls-compile"
>
Save
&
Compile to edX XML
</button>
<button
type=
"reset"
class=
"hls-compile"
>
Save
&
Compile to edX XML
</button>
...
...
common/lib/xmodule/xmodule/capa_module.py
View file @
01e15c1e
...
@@ -99,6 +99,7 @@ class CapaFields(object):
...
@@ -99,6 +99,7 @@ class CapaFields(object):
seed
=
StringyInteger
(
help
=
"Random seed for this student"
,
scope
=
Scope
.
user_state
)
seed
=
StringyInteger
(
help
=
"Random seed for this student"
,
scope
=
Scope
.
user_state
)
weight
=
StringyFloat
(
help
=
"How much to weight this problem by"
,
scope
=
Scope
.
settings
)
weight
=
StringyFloat
(
help
=
"How much to weight this problem by"
,
scope
=
Scope
.
settings
)
markdown
=
String
(
help
=
"Markdown source of this module"
,
scope
=
Scope
.
settings
)
markdown
=
String
(
help
=
"Markdown source of this module"
,
scope
=
Scope
.
settings
)
source_code
=
String
(
help
=
"Source code for LaTeX and Word problems. This feature is not well-supported."
,
scope
=
Scope
.
settings
)
class
CapaModule
(
CapaFields
,
XModule
):
class
CapaModule
(
CapaFields
,
XModule
):
...
...
common/lib/xmodule/xmodule/discussion_module.py
View file @
01e15c1e
...
@@ -3,6 +3,7 @@ from pkg_resources import resource_string, resource_listdir
...
@@ -3,6 +3,7 @@ from pkg_resources import resource_string, resource_listdir
from
xmodule.x_module
import
XModule
from
xmodule.x_module
import
XModule
from
xmodule.raw_module
import
RawDescriptor
from
xmodule.raw_module
import
RawDescriptor
from
xmodule.editing_module
import
MetadataOnlyEditingDescriptor
from
xblock.core
import
String
,
Scope
from
xblock.core
import
String
,
Scope
...
@@ -28,7 +29,7 @@ class DiscussionModule(DiscussionFields, XModule):
...
@@ -28,7 +29,7 @@ class DiscussionModule(DiscussionFields, XModule):
return
self
.
system
.
render_template
(
'discussion/_discussion_module.html'
,
context
)
return
self
.
system
.
render_template
(
'discussion/_discussion_module.html'
,
context
)
class
DiscussionDescriptor
(
DiscussionFields
,
RawDescriptor
):
class
DiscussionDescriptor
(
DiscussionFields
,
MetadataOnlyEditingDescriptor
,
RawDescriptor
):
module_class
=
DiscussionModule
module_class
=
DiscussionModule
template_dir_name
=
"discussion"
template_dir_name
=
"discussion"
...
...
common/lib/xmodule/xmodule/editing_module.py
View file @
01e15c1e
...
@@ -41,6 +41,18 @@ class XMLEditingDescriptor(EditingDescriptor):
...
@@ -41,6 +41,18 @@ class XMLEditingDescriptor(EditingDescriptor):
js_module_name
=
"XMLEditingDescriptor"
js_module_name
=
"XMLEditingDescriptor"
class
MetadataOnlyEditingDescriptor
(
EditingDescriptor
):
"""
Module which only provides an editing interface for the metadata, it does
not expose a UI for editing the module data
"""
js
=
{
'coffee'
:
[
resource_string
(
__name__
,
'js/src/raw/edit/metadata-only.coffee'
)]}
js_module_name
=
"MetadataOnlyEditingDescriptor"
mako_template
=
"widgets/metadata-only-edit.html"
class
JSONEditingDescriptor
(
EditingDescriptor
):
class
JSONEditingDescriptor
(
EditingDescriptor
):
"""
"""
Module that provides a raw editing view of its data as XML. It does not perform
Module that provides a raw editing view of its data as XML. It does not perform
...
...
common/lib/xmodule/xmodule/html_module.py
View file @
01e15c1e
...
@@ -118,8 +118,8 @@ class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor):
...
@@ -118,8 +118,8 @@ class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor):
with
system
.
resources_fs
.
open
(
filepath
)
as
file
:
with
system
.
resources_fs
.
open
(
filepath
)
as
file
:
html
=
file
.
read
()
.
decode
(
'utf-8'
)
html
=
file
.
read
()
.
decode
(
'utf-8'
)
# Log a warning if we can't parse the file, but don't error
# Log a warning if we can't parse the file, but don't error
if
not
check_html
(
html
):
if
not
check_html
(
html
)
and
len
(
html
)
>
0
:
msg
=
"Couldn't parse html in {0}
."
.
format
(
filepath
)
msg
=
"Couldn't parse html in {0}
, content = {1}"
.
format
(
filepath
,
html
)
log
.
warning
(
msg
)
log
.
warning
(
msg
)
system
.
error_tracker
(
"Warning: "
+
msg
)
system
.
error_tracker
(
"Warning: "
+
msg
)
...
@@ -156,7 +156,8 @@ class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor):
...
@@ -156,7 +156,8 @@ class HtmlDescriptor(HtmlFields, XmlDescriptor, EditingDescriptor):
resource_fs
.
makedir
(
os
.
path
.
dirname
(
filepath
),
recursive
=
True
,
allow_recreate
=
True
)
resource_fs
.
makedir
(
os
.
path
.
dirname
(
filepath
),
recursive
=
True
,
allow_recreate
=
True
)
with
resource_fs
.
open
(
filepath
,
'w'
)
as
file
:
with
resource_fs
.
open
(
filepath
,
'w'
)
as
file
:
file
.
write
(
self
.
data
.
encode
(
'utf-8'
))
html_data
=
self
.
data
.
encode
(
'utf-8'
)
file
.
write
(
html_data
)
# write out the relative name
# write out the relative name
relname
=
path
(
pathname
)
.
basename
()
relname
=
path
(
pathname
)
.
basename
()
...
...
common/lib/xmodule/xmodule/js/src/raw/edit/metadata-only.coffee
0 → 100644
View file @
01e15c1e
class
@
MetadataOnlyEditingDescriptor
extends
XModule
.
Descriptor
constructor
:
(
@
element
)
->
save
:
->
data
:
null
common/lib/xmodule/xmodule/modulestore/__init__.py
View file @
01e15c1e
...
@@ -252,7 +252,6 @@ class Location(_LocationBase):
...
@@ -252,7 +252,6 @@ class Location(_LocationBase):
def
__repr__
(
self
):
def
__repr__
(
self
):
return
"Location
%
s"
%
repr
(
tuple
(
self
))
return
"Location
%
s"
%
repr
(
tuple
(
self
))
@property
@property
def
course_id
(
self
):
def
course_id
(
self
):
"""Return the ID of the Course that this item belongs to by looking
"""Return the ID of the Course that this item belongs to by looking
...
@@ -414,7 +413,6 @@ class ModuleStore(object):
...
@@ -414,7 +413,6 @@ class ModuleStore(object):
return
courses
return
courses
class
ModuleStoreBase
(
ModuleStore
):
class
ModuleStoreBase
(
ModuleStore
):
'''
'''
Implement interface functionality that can be shared.
Implement interface functionality that can be shared.
...
@@ -425,6 +423,7 @@ class ModuleStoreBase(ModuleStore):
...
@@ -425,6 +423,7 @@ class ModuleStoreBase(ModuleStore):
'''
'''
self
.
_location_errors
=
{}
# location -> ErrorLog
self
.
_location_errors
=
{}
# location -> ErrorLog
self
.
metadata_inheritance_cache
=
None
self
.
metadata_inheritance_cache
=
None
self
.
modulestore_update_signal
=
None
# can be set by runtime to route notifications of datastore changes
def
_get_errorlog
(
self
,
location
):
def
_get_errorlog
(
self
,
location
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/draft.py
View file @
01e15c1e
...
@@ -3,7 +3,6 @@ from datetime import datetime
...
@@ -3,7 +3,6 @@ from datetime import datetime
from
.
import
ModuleStoreBase
,
Location
,
namedtuple_to_son
from
.
import
ModuleStoreBase
,
Location
,
namedtuple_to_son
from
.exceptions
import
ItemNotFoundError
from
.exceptions
import
ItemNotFoundError
from
.inheritance
import
own_metadata
from
.inheritance
import
own_metadata
import
logging
DRAFT
=
'draft'
DRAFT
=
'draft'
...
@@ -107,7 +106,7 @@ class DraftModuleStore(ModuleStoreBase):
...
@@ -107,7 +106,7 @@ class DraftModuleStore(ModuleStoreBase):
"""
"""
return
wrap_draft
(
super
(
DraftModuleStore
,
self
)
.
clone_item
(
source
,
as_draft
(
location
)))
return
wrap_draft
(
super
(
DraftModuleStore
,
self
)
.
clone_item
(
source
,
as_draft
(
location
)))
def
update_item
(
self
,
location
,
data
):
def
update_item
(
self
,
location
,
data
,
allow_not_found
=
False
):
"""
"""
Set the data in the item specified by the location to
Set the data in the item specified by the location to
data
data
...
@@ -116,9 +115,13 @@ class DraftModuleStore(ModuleStoreBase):
...
@@ -116,9 +115,13 @@ class DraftModuleStore(ModuleStoreBase):
data: A nested dictionary of problem data
data: A nested dictionary of problem data
"""
"""
draft_loc
=
as_draft
(
location
)
draft_loc
=
as_draft
(
location
)
draft_item
=
self
.
get_item
(
location
)
try
:
if
not
getattr
(
draft_item
,
'is_draft'
,
False
):
draft_item
=
self
.
get_item
(
location
)
self
.
clone_item
(
location
,
draft_loc
)
if
not
getattr
(
draft_item
,
'is_draft'
,
False
):
self
.
clone_item
(
location
,
draft_loc
)
except
ItemNotFoundError
,
e
:
if
not
allow_not_found
:
raise
e
return
super
(
DraftModuleStore
,
self
)
.
update_item
(
draft_loc
,
data
)
return
super
(
DraftModuleStore
,
self
)
.
update_item
(
draft_loc
,
data
)
...
@@ -164,7 +167,6 @@ class DraftModuleStore(ModuleStoreBase):
...
@@ -164,7 +167,6 @@ class DraftModuleStore(ModuleStoreBase):
"""
"""
return
super
(
DraftModuleStore
,
self
)
.
delete_item
(
as_draft
(
location
))
return
super
(
DraftModuleStore
,
self
)
.
delete_item
(
as_draft
(
location
))
def
get_parent_locations
(
self
,
location
,
course_id
):
def
get_parent_locations
(
self
,
location
,
course_id
):
'''Find all locations that are the parents of this location. Needed
'''Find all locations that are the parents of this location. Needed
for path_to_location().
for path_to_location().
...
@@ -178,6 +180,7 @@ class DraftModuleStore(ModuleStoreBase):
...
@@ -178,6 +180,7 @@ class DraftModuleStore(ModuleStoreBase):
Save a current draft to the underlying modulestore
Save a current draft to the underlying modulestore
"""
"""
draft
=
self
.
get_item
(
location
)
draft
=
self
.
get_item
(
location
)
draft
.
cms
.
published_date
=
datetime
.
utcnow
()
draft
.
cms
.
published_date
=
datetime
.
utcnow
()
draft
.
cms
.
published_by
=
published_by_id
draft
.
cms
.
published_by
=
published_by_id
super
(
DraftModuleStore
,
self
)
.
update_item
(
location
,
draft
.
_model_data
.
_kvs
.
_data
)
super
(
DraftModuleStore
,
self
)
.
update_item
(
location
,
draft
.
_model_data
.
_kvs
.
_data
)
...
@@ -221,6 +224,6 @@ class DraftModuleStore(ModuleStoreBase):
...
@@ -221,6 +224,6 @@ class DraftModuleStore(ModuleStoreBase):
# convert the dict - which is used for look ups - back into a list
# convert the dict - which is used for look ups - back into a list
for
key
,
value
in
to_process_dict
.
iteritems
():
for
key
,
value
in
to_process_dict
.
iteritems
():
queried_children
.
append
(
value
)
queried_children
.
append
(
value
)
return
queried_children
return
queried_children
common/lib/xmodule/xmodule/modulestore/mongo.py
View file @
01e15c1e
...
@@ -9,6 +9,7 @@ from itertools import repeat
...
@@ -9,6 +9,7 @@ from itertools import repeat
from
path
import
path
from
path
import
path
from
datetime
import
datetime
from
datetime
import
datetime
from
operator
import
attrgetter
from
operator
import
attrgetter
from
uuid
import
uuid4
from
importlib
import
import_module
from
importlib
import
import_module
from
xmodule.errortracker
import
null_error_tracker
,
exc_info_to_str
from
xmodule.errortracker
import
null_error_tracker
,
exc_info_to_str
...
@@ -30,6 +31,10 @@ log = logging.getLogger(__name__)
...
@@ -30,6 +31,10 @@ log = logging.getLogger(__name__)
# there is only one revision for each item. Once we start versioning inside the CMS,
# there is only one revision for each item. Once we start versioning inside the CMS,
# that assumption will have to change
# that assumption will have to change
def
get_course_id_no_run
(
location
):
'''
'''
return
"/"
.
join
([
location
.
org
,
location
.
course
])
class
MongoKeyValueStore
(
KeyValueStore
):
class
MongoKeyValueStore
(
KeyValueStore
):
"""
"""
...
@@ -333,7 +338,7 @@ class MongoModuleStore(ModuleStoreBase):
...
@@ -333,7 +338,7 @@ class MongoModuleStore(ModuleStoreBase):
'''
'''
key
=
metadata_cache_key
(
location
)
key
=
metadata_cache_key
(
location
)
tree
=
{}
tree
=
{}
if
not
force_refresh
:
if
not
force_refresh
:
# see if we are first in the request cache (if present)
# see if we are first in the request cache (if present)
if
self
.
request_cache
is
not
None
and
key
in
self
.
request_cache
.
data
.
get
(
'metadata_inheritance'
,
{}):
if
self
.
request_cache
is
not
None
and
key
in
self
.
request_cache
.
data
.
get
(
'metadata_inheritance'
,
{}):
...
@@ -348,7 +353,7 @@ class MongoModuleStore(ModuleStoreBase):
...
@@ -348,7 +353,7 @@ class MongoModuleStore(ModuleStoreBase):
if
not
tree
:
if
not
tree
:
# if not in subsystem, or we are on force refresh, then we have to compute
# if not in subsystem, or we are on force refresh, then we have to compute
tree
=
self
.
compute_metadata_inheritance_tree
(
location
)
tree
=
self
.
compute_metadata_inheritance_tree
(
location
)
# now write out computed tree to caching subsystem (e.g. memcached), if available
# now write out computed tree to caching subsystem (e.g. memcached), if available
if
self
.
metadata_inheritance_cache_subsystem
is
not
None
:
if
self
.
metadata_inheritance_cache_subsystem
is
not
None
:
self
.
metadata_inheritance_cache_subsystem
.
set
(
key
,
tree
)
self
.
metadata_inheritance_cache_subsystem
.
set
(
key
,
tree
)
...
@@ -541,8 +546,15 @@ class MongoModuleStore(ModuleStoreBase):
...
@@ -541,8 +546,15 @@ class MongoModuleStore(ModuleStoreBase):
Clone a new item that is a copy of the item at the location `source`
Clone a new item that is a copy of the item at the location `source`
and writes it to `location`
and writes it to `location`
"""
"""
item
=
None
try
:
try
:
source_item
=
self
.
collection
.
find_one
(
location_to_query
(
source
))
source_item
=
self
.
collection
.
find_one
(
location_to_query
(
source
))
# allow for some programmatically generated substitutions in metadata, e.g. Discussion_id's should be auto-generated
for
key
in
source_item
[
'metadata'
]
.
keys
():
if
source_item
[
'metadata'
][
key
]
==
'$$GUID$$'
:
source_item
[
'metadata'
][
key
]
=
uuid4
()
.
hex
source_item
[
'_id'
]
=
Location
(
location
)
.
dict
()
source_item
[
'_id'
]
=
Location
(
location
)
.
dict
()
self
.
collection
.
insert
(
self
.
collection
.
insert
(
source_item
,
source_item
,
...
@@ -566,12 +578,19 @@ class MongoModuleStore(ModuleStoreBase):
...
@@ -566,12 +578,19 @@ class MongoModuleStore(ModuleStoreBase):
course
.
tabs
=
existing_tabs
course
.
tabs
=
existing_tabs
self
.
update_metadata
(
course
.
location
,
course
.
_model_data
.
_kvs
.
_metadata
)
self
.
update_metadata
(
course
.
location
,
course
.
_model_data
.
_kvs
.
_metadata
)
return
item
except
pymongo
.
errors
.
DuplicateKeyError
:
except
pymongo
.
errors
.
DuplicateKeyError
:
raise
DuplicateItemError
(
location
)
raise
DuplicateItemError
(
location
)
# recompute (and update) the metadata inheritance tree which is cached
# recompute (and update) the metadata inheritance tree which is cached
self
.
refresh_cached_metadata_inheritance_tree
(
Location
(
location
))
self
.
refresh_cached_metadata_inheritance_tree
(
Location
(
location
))
self
.
fire_updated_modulestore_signal
(
get_course_id_no_run
(
Location
(
location
)),
Location
(
location
))
return
item
def
fire_updated_modulestore_signal
(
self
,
course_id
,
location
):
if
self
.
modulestore_update_signal
is
not
None
:
self
.
modulestore_update_signal
.
send
(
self
,
modulestore
=
self
,
course_id
=
course_id
,
location
=
location
)
def
get_course_for_item
(
self
,
location
,
depth
=
0
):
def
get_course_for_item
(
self
,
location
,
depth
=
0
):
'''
'''
...
@@ -643,6 +662,8 @@ class MongoModuleStore(ModuleStoreBase):
...
@@ -643,6 +662,8 @@ class MongoModuleStore(ModuleStoreBase):
self
.
_update_single_item
(
location
,
{
'definition.children'
:
children
})
self
.
_update_single_item
(
location
,
{
'definition.children'
:
children
})
# recompute (and update) the metadata inheritance tree which is cached
# recompute (and update) the metadata inheritance tree which is cached
self
.
refresh_cached_metadata_inheritance_tree
(
Location
(
location
))
self
.
refresh_cached_metadata_inheritance_tree
(
Location
(
location
))
# fire signal that we've written to DB
self
.
fire_updated_modulestore_signal
(
get_course_id_no_run
(
Location
(
location
)),
Location
(
location
))
def
update_metadata
(
self
,
location
,
metadata
):
def
update_metadata
(
self
,
location
,
metadata
):
"""
"""
...
@@ -669,6 +690,7 @@ class MongoModuleStore(ModuleStoreBase):
...
@@ -669,6 +690,7 @@ class MongoModuleStore(ModuleStoreBase):
self
.
_update_single_item
(
location
,
{
'metadata'
:
metadata
})
self
.
_update_single_item
(
location
,
{
'metadata'
:
metadata
})
# recompute (and update) the metadata inheritance tree which is cached
# recompute (and update) the metadata inheritance tree which is cached
self
.
refresh_cached_metadata_inheritance_tree
(
loc
)
self
.
refresh_cached_metadata_inheritance_tree
(
loc
)
self
.
fire_updated_modulestore_signal
(
get_course_id_no_run
(
Location
(
location
)),
Location
(
location
))
def
delete_item
(
self
,
location
):
def
delete_item
(
self
,
location
):
"""
"""
...
@@ -692,6 +714,7 @@ class MongoModuleStore(ModuleStoreBase):
...
@@ -692,6 +714,7 @@ class MongoModuleStore(ModuleStoreBase):
safe
=
self
.
collection
.
safe
)
safe
=
self
.
collection
.
safe
)
# recompute (and update) the metadata inheritance tree which is cached
# recompute (and update) the metadata inheritance tree which is cached
self
.
refresh_cached_metadata_inheritance_tree
(
Location
(
location
))
self
.
refresh_cached_metadata_inheritance_tree
(
Location
(
location
))
self
.
fire_updated_modulestore_signal
(
get_course_id_no_run
(
Location
(
location
)),
Location
(
location
))
def
get_parent_locations
(
self
,
location
,
course_id
):
def
get_parent_locations
(
self
,
location
,
course_id
):
'''Find all locations that are the parents of this location in this
'''Find all locations that are the parents of this location in this
...
...
common/lib/xmodule/xmodule/modulestore/xml_exporter.py
View file @
01e15c1e
import
logging
import
logging
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.inheritance
import
own_metadata
from
xmodule.modulestore.inheritance
import
own_metadata
from
fs.osfs
import
OSFS
from
fs.osfs
import
OSFS
from
json
import
dumps
from
json
import
dumps
def
export_to_xml
(
modulestore
,
contentstore
,
course_location
,
root_dir
,
course_dir
):
def
export_to_xml
(
modulestore
,
contentstore
,
course_location
,
root_dir
,
course_dir
,
draft_modulestore
=
None
):
course
=
modulestore
.
get_item
(
course_location
)
course
=
modulestore
.
get_item
(
course_location
)
...
@@ -40,6 +39,24 @@ def export_to_xml(modulestore, contentstore, course_location, root_dir, course_d
...
@@ -40,6 +39,24 @@ def export_to_xml(modulestore, contentstore, course_location, root_dir, course_d
policy
=
{
'course/'
+
course
.
location
.
name
:
own_metadata
(
course
)}
policy
=
{
'course/'
+
course
.
location
.
name
:
own_metadata
(
course
)}
course_policy
.
write
(
dumps
(
policy
))
course_policy
.
write
(
dumps
(
policy
))
# export draft content
# NOTE: this code assumes that verticals are the top most draftable container
# should we change the application, then this assumption will no longer
# be valid
if
draft_modulestore
is
not
None
:
draft_verticals
=
draft_modulestore
.
get_items
([
None
,
course_location
.
org
,
course_location
.
course
,
'vertical'
,
None
,
'draft'
])
if
len
(
draft_verticals
)
>
0
:
draft_course_dir
=
export_fs
.
makeopendir
(
'drafts'
)
for
draft_vertical
in
draft_verticals
:
parent_locs
=
draft_modulestore
.
get_parent_locations
(
draft_vertical
.
location
,
course
.
location
.
course_id
)
logging
.
debug
(
'parent_locs = {0}'
.
format
(
parent_locs
))
draft_vertical
.
xml_attributes
[
'parent_sequential_url'
]
=
Location
(
parent_locs
[
0
])
.
url
()
sequential
=
modulestore
.
get_item
(
Location
(
parent_locs
[
0
]))
index
=
sequential
.
children
.
index
(
draft_vertical
.
location
.
url
())
draft_vertical
.
xml_attributes
[
'index_in_children_list'
]
=
str
(
index
)
draft_vertical
.
export_to_xml
(
draft_course_dir
)
def
export_extra_content
(
export_fs
,
modulestore
,
course_location
,
category_type
,
dirname
,
file_suffix
=
''
):
def
export_extra_content
(
export_fs
,
modulestore
,
course_location
,
category_type
,
dirname
,
file_suffix
=
''
):
query_loc
=
Location
(
'i4x'
,
course_location
.
org
,
course_location
.
course
,
category_type
,
None
)
query_loc
=
Location
(
'i4x'
,
course_location
.
org
,
course_location
.
course
,
category_type
,
None
)
...
...
common/lib/xmodule/xmodule/modulestore/xml_importer.py
View file @
01e15c1e
This diff is collapsed.
Click to expand it.
common/lib/xmodule/xmodule/raw_module.py
View file @
01e15c1e
...
@@ -29,6 +29,6 @@ class RawDescriptor(XmlDescriptor, XMLEditingDescriptor):
...
@@ -29,6 +29,6 @@ class RawDescriptor(XmlDescriptor, XMLEditingDescriptor):
line
,
offset
=
err
.
position
line
,
offset
=
err
.
position
msg
=
(
"Unable to create xml for problem {loc}. "
msg
=
(
"Unable to create xml for problem {loc}. "
"Context: '{context}'"
.
format
(
"Context: '{context}'"
.
format
(
context
=
lines
[
line
-
1
][
offset
-
40
:
offset
+
40
],
context
=
lines
[
line
-
1
][
offset
-
40
:
offset
+
40
],
loc
=
self
.
location
))
loc
=
self
.
location
))
raise
Exception
,
msg
,
sys
.
exc_info
()[
2
]
raise
Exception
,
msg
,
sys
.
exc_info
()[
2
]
common/lib/xmodule/xmodule/templates/discussion/default.yaml
View file @
01e15c1e
...
@@ -2,8 +2,8 @@
...
@@ -2,8 +2,8 @@
metadata
:
metadata
:
display_name
:
Discussion Tag
display_name
:
Discussion Tag
for
:
Topic-Level Student-Visible Label
for
:
Topic-Level Student-Visible Label
id
:
6002x_group_discussion_by_this
id
:
$$GUID$$
discussion_category
:
Week 1
discussion_category
:
Week 1
data
:
|
data
:
|
<discussion
for="Topic-Level Student-Visible Label" id="6002x_group_discussion_by_this" discussion_category="Week 1"
/>
<discussion />
children
:
[]
children
:
[]
common/lib/xmodule/xmodule/templates/html/latex_html.yaml
View file @
01e15c1e
---
---
metadata
:
metadata
:
display_name
:
E-text Written in LaTeX
display_name
:
E-text Written in LaTeX
source_processor_url
:
https://qisx.mit.edu:5443/latex2edx
source_code
:
|
source_code
:
|
\subsection{Example of E-text in LaTeX}
\subsection{Example of E-text in LaTeX}
...
...
common/lib/xmodule/xmodule/templates/problem/latex_problem.yaml
View file @
01e15c1e
---
---
metadata
:
metadata
:
display_name
:
Problem Written in LaTeX
display_name
:
Problem Written in LaTeX
source_processor_url
:
https://studio-input-filter.mitx.mit.edu/latex2edx
source_code
:
|
source_code
:
|
% Nearly any kind of edX problem can be authored using Latex as
% Nearly any kind of edX problem can be authored using Latex as
% the source language. Write latex as usual, including equations. The
% the source language. Write latex as usual, including equations. The
...
...
common/lib/xmodule/xmodule/templates/problem/problem_with_hint.yaml
View file @
01e15c1e
---
---
metadata
:
metadata
:
display_name
:
Problem with Adaptive Hint
display_name
:
Problem with Adaptive Hint
source_processor_url
:
https://qisx.mit.edu:5443/latex2edx
source_code
:
|
source_code
:
|
\subsection{Problem With Adaptive Hint}
\subsection{Problem With Adaptive Hint}
...
...
common/lib/xmodule/xmodule/x_module.py
View file @
01e15c1e
...
@@ -340,7 +340,9 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
...
@@ -340,7 +340,9 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
# cdodge: this is a list of metadata names which are 'system' metadata
# cdodge: this is a list of metadata names which are 'system' metadata
# and should not be edited by an end-user
# and should not be edited by an end-user
system_metadata_fields
=
[
'data_dir'
,
'published_date'
,
'published_by'
,
'is_draft'
,
'xml_attributes'
]
system_metadata_fields
=
[
'data_dir'
,
'published_date'
,
'published_by'
,
'is_draft'
,
'discussion_id'
,
'xml_attributes'
]
# A list of descriptor attributes that must be equal for the descriptors to
# A list of descriptor attributes that must be equal for the descriptors to
# be equal
# be equal
...
...
common/lib/xmodule/xmodule/xml_module.py
View file @
01e15c1e
...
@@ -110,8 +110,7 @@ class XmlDescriptor(XModuleDescriptor):
...
@@ -110,8 +110,7 @@ class XmlDescriptor(XModuleDescriptor):
'name'
,
'slug'
)
'name'
,
'slug'
)
metadata_to_strip
=
(
'data_dir'
,
metadata_to_strip
=
(
'data_dir'
,
# cdodge: @TODO: We need to figure out a way to export out 'tabs' and 'grading_policy' which is on the course
'tabs'
,
'grading_policy'
,
'published_by'
,
'published_date'
,
'tabs'
,
'grading_policy'
,
'is_draft'
,
'published_by'
,
'published_date'
,
'discussion_blackouts'
,
'testcenter_info'
,
'discussion_blackouts'
,
'testcenter_info'
,
# VS[compat] -- remove the below attrs once everything is in the CMS
# VS[compat] -- remove the below attrs once everything is in the CMS
'course'
,
'org'
,
'url_name'
,
'filename'
,
'course'
,
'org'
,
'url_name'
,
'filename'
,
...
@@ -135,7 +134,7 @@ class XmlDescriptor(XModuleDescriptor):
...
@@ -135,7 +134,7 @@ class XmlDescriptor(XModuleDescriptor):
'graded'
:
bool_map
,
'graded'
:
bool_map
,
'hide_progress_tab'
:
bool_map
,
'hide_progress_tab'
:
bool_map
,
'allow_anonymous'
:
bool_map
,
'allow_anonymous'
:
bool_map
,
'allow_anonymous_to_peers'
:
bool_map
'allow_anonymous_to_peers'
:
bool_map
,
}
}
...
...
lms/djangoapps/courseware/tests/test_login.py
View file @
01e15c1e
from
django.test
import
TestCase
from
django.test
import
TestCase
from
django.test.client
import
Client
from
django.test.client
import
Client
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.contrib.auth.models
import
User
from
factories
import
UserFactory
,
RegistrationFactory
,
UserProfileFactory
from
student.models
import
Registration
,
UserProfile
import
json
import
json
class
LoginTest
(
TestCase
):
class
LoginTest
(
TestCase
):
'''
'''
Test student.views.login_user() view
Test student.views.login_user() view
'''
'''
def
setUp
(
self
):
def
setUp
(
self
):
# Create one user and save it to the database
# Create one user and save it to the database
self
.
user
=
User
.
objects
.
create_user
(
'test'
,
'test@edx.org'
,
'test_password
'
)
self
.
user
=
User
Factory
.
build
(
username
=
'test'
,
email
=
'test@edx.org
'
)
self
.
user
.
is_active
=
True
self
.
user
.
set_password
(
'test_password'
)
self
.
user
.
save
()
self
.
user
.
save
()
# Create a registration for the user
# Create a registration for the user
Registration
()
.
register
(
self
.
user
)
registration
=
RegistrationFactory
(
user
=
self
.
user
)
registration
.
register
(
self
.
user
)
registration
.
activate
()
# Create a profile for the user
# Create a profile for the user
UserProfile
(
user
=
self
.
user
)
.
save
(
)
UserProfile
Factory
(
user
=
self
.
user
)
# Create the test client
# Create the test client
self
.
client
=
Client
()
self
.
client
=
Client
()
...
@@ -42,19 +43,17 @@ class LoginTest(TestCase):
...
@@ -42,19 +43,17 @@ class LoginTest(TestCase):
response
=
self
.
_login_response
(
unicode_email
,
'test_password'
)
response
=
self
.
_login_response
(
unicode_email
,
'test_password'
)
self
.
_assert_response
(
response
,
success
=
True
)
self
.
_assert_response
(
response
,
success
=
True
)
def
test_login_fail_no_user_exists
(
self
):
def
test_login_fail_no_user_exists
(
self
):
response
=
self
.
_login_response
(
'not_a_user@edx.org'
,
'test_password'
)
response
=
self
.
_login_response
(
'not_a_user@edx.org'
,
'test_password'
)
self
.
_assert_response
(
response
,
success
=
False
,
self
.
_assert_response
(
response
,
success
=
False
,
value
=
'Email or password is incorrect'
)
value
=
'Email or password is incorrect'
)
def
test_login_fail_wrong_password
(
self
):
def
test_login_fail_wrong_password
(
self
):
response
=
self
.
_login_response
(
'test@edx.org'
,
'wrong_password'
)
response
=
self
.
_login_response
(
'test@edx.org'
,
'wrong_password'
)
self
.
_assert_response
(
response
,
success
=
False
,
self
.
_assert_response
(
response
,
success
=
False
,
value
=
'Email or password is incorrect'
)
value
=
'Email or password is incorrect'
)
def
test_login_not_activated
(
self
):
def
test_login_not_activated
(
self
):
# De-activate the user
# De-activate the user
self
.
user
.
is_active
=
False
self
.
user
.
is_active
=
False
self
.
user
.
save
()
self
.
user
.
save
()
...
@@ -62,8 +61,7 @@ class LoginTest(TestCase):
...
@@ -62,8 +61,7 @@ class LoginTest(TestCase):
# Should now be unable to login
# Should now be unable to login
response
=
self
.
_login_response
(
'test@edx.org'
,
'test_password'
)
response
=
self
.
_login_response
(
'test@edx.org'
,
'test_password'
)
self
.
_assert_response
(
response
,
success
=
False
,
self
.
_assert_response
(
response
,
success
=
False
,
value
=
"This account has not been activated"
)
value
=
"This account has not been activated"
)
def
test_login_unicode_email
(
self
):
def
test_login_unicode_email
(
self
):
unicode_email
=
u'test@edx.org'
+
unichr
(
40960
)
unicode_email
=
u'test@edx.org'
+
unichr
(
40960
)
...
@@ -95,13 +93,13 @@ class LoginTest(TestCase):
...
@@ -95,13 +93,13 @@ class LoginTest(TestCase):
try
:
try
:
response_dict
=
json
.
loads
(
response
.
content
)
response_dict
=
json
.
loads
(
response
.
content
)
except
ValueError
:
except
ValueError
:
self
.
fail
(
"Could not parse response content as JSON:
%
s"
self
.
fail
(
"Could not parse response content as JSON:
%
s"
%
str
(
response
.
content
))
%
str
(
response
.
content
))
if
success
is
not
None
:
if
success
is
not
None
:
self
.
assertEqual
(
response_dict
[
'success'
],
success
)
self
.
assertEqual
(
response_dict
[
'success'
],
success
)
if
value
is
not
None
:
if
value
is
not
None
:
msg
=
(
"'
%
s' did not contain '
%
s'"
%
msg
=
(
"'
%
s' did not contain '
%
s'"
%
(
str
(
response_dict
[
'value'
]),
str
(
value
)))
(
str
(
response_dict
[
'value'
]),
str
(
value
)))
self
.
assertTrue
(
value
in
response_dict
[
'value'
],
msg
)
self
.
assertTrue
(
value
in
response_dict
[
'value'
],
msg
)
lms/djangoapps/django_comment_client/management/commands/seed_permissions_roles.py
View file @
01e15c1e
from
django.core.management.base
import
BaseCommand
,
CommandError
from
django.core.management.base
import
BaseCommand
,
CommandError
from
django_comment_client.models
import
Permission
,
Role
from
django_comment_client.models
import
Role
class
Command
(
BaseCommand
):
class
Command
(
BaseCommand
):
...
@@ -12,18 +12,19 @@ class Command(BaseCommand):
...
@@ -12,18 +12,19 @@ class Command(BaseCommand):
if
len
(
args
)
>
1
:
if
len
(
args
)
>
1
:
raise
CommandError
(
"Too many arguments"
)
raise
CommandError
(
"Too many arguments"
)
course_id
=
args
[
0
]
course_id
=
args
[
0
]
administrator_role
=
Role
.
objects
.
get_or_create
(
name
=
"Administrator"
,
course_id
=
course_id
)[
0
]
administrator_role
=
Role
.
objects
.
get_or_create
(
name
=
"Administrator"
,
course_id
=
course_id
)[
0
]
moderator_role
=
Role
.
objects
.
get_or_create
(
name
=
"Moderator"
,
course_id
=
course_id
)[
0
]
moderator_role
=
Role
.
objects
.
get_or_create
(
name
=
"Moderator"
,
course_id
=
course_id
)[
0
]
community_ta_role
=
Role
.
objects
.
get_or_create
(
name
=
"Community TA"
,
course_id
=
course_id
)[
0
]
community_ta_role
=
Role
.
objects
.
get_or_create
(
name
=
"Community TA"
,
course_id
=
course_id
)[
0
]
student_role
=
Role
.
objects
.
get_or_create
(
name
=
"Student"
,
course_id
=
course_id
)[
0
]
student_role
=
Role
.
objects
.
get_or_create
(
name
=
"Student"
,
course_id
=
course_id
)[
0
]
for
per
in
[
"vote"
,
"update_thread"
,
"follow_thread"
,
"unfollow_thread"
,
for
per
in
[
"vote"
,
"update_thread"
,
"follow_thread"
,
"unfollow_thread"
,
"update_comment"
,
"create_sub_comment"
,
"unvote"
,
"create_thread"
,
"update_comment"
,
"create_sub_comment"
,
"unvote"
,
"create_thread"
,
"follow_commentable"
,
"unfollow_commentable"
,
"create_comment"
,
]:
"follow_commentable"
,
"unfollow_commentable"
,
"create_comment"
,
]:
student_role
.
add_permission
(
per
)
student_role
.
add_permission
(
per
)
for
per
in
[
"edit_content"
,
"delete_thread"
,
"openclose_thread"
,
for
per
in
[
"edit_content"
,
"delete_thread"
,
"openclose_thread"
,
"endorse_comment"
,
"delete_comment"
,
"see_all_cohorts"
]:
"endorse_comment"
,
"delete_comment"
,
"see_all_cohorts"
]:
moderator_role
.
add_permission
(
per
)
moderator_role
.
add_permission
(
per
)
for
per
in
[
"manage_moderator"
]:
for
per
in
[
"manage_moderator"
]:
...
...
lms/djangoapps/django_comment_client/tests/mock_cs_server/__init__.py
0 → 100644
View file @
01e15c1e
lms/djangoapps/django_comment_client/tests/mock_cs_server/mock_cs_server.py
0 → 100644
View file @
01e15c1e
from
BaseHTTPServer
import
HTTPServer
,
BaseHTTPRequestHandler
import
json
from
logging
import
getLogger
logger
=
getLogger
(
__name__
)
class
MockCommentServiceRequestHandler
(
BaseHTTPRequestHandler
):
'''
A handler for Comment Service POST requests.
'''
protocol
=
"HTTP/1.0"
def
do_POST
(
self
):
'''
Handle a POST request from the client
Used by the APIs for comment threads, commentables, comments,
subscriptions, commentables, users
'''
# Retrieve the POST data into a dict.
# It should have been sent in json format
length
=
int
(
self
.
headers
.
getheader
(
'content-length'
))
data_string
=
self
.
rfile
.
read
(
length
)
post_dict
=
json
.
loads
(
data_string
)
# Log the request
logger
.
debug
(
"Comment Service received POST request
%
s to path
%
s"
%
(
json
.
dumps
(
post_dict
),
self
.
path
))
# Every good post has at least an API key
if
'api_key'
in
post_dict
:
response
=
self
.
server
.
_response_str
# Log the response
logger
.
debug
(
"Comment Service: sending response
%
s"
%
json
.
dumps
(
response
))
# Send a response back to the client
self
.
send_response
(
200
)
self
.
send_header
(
'Content-type'
,
'application/json'
)
self
.
end_headers
()
self
.
wfile
.
write
(
response
)
else
:
# Respond with failure
self
.
send_response
(
500
,
'Bad Request: does not contain API key'
)
self
.
send_header
(
'Content-type'
,
'text/plain'
)
self
.
end_headers
()
return
False
class
MockCommentServiceServer
(
HTTPServer
):
'''
A mock Comment Service server that responds
to POST requests to localhost.
'''
def
__init__
(
self
,
port_num
,
response
=
{
'username'
:
'new'
,
'external_id'
:
1
}):
'''
Initialize the mock Comment Service server instance.
*port_num* is the localhost port to listen to
*response* is a dictionary that will be JSON-serialized
and sent in response to comment service requests.
'''
self
.
_response_str
=
json
.
dumps
(
response
)
handler
=
MockCommentServiceRequestHandler
address
=
(
''
,
port_num
)
HTTPServer
.
__init__
(
self
,
address
,
handler
)
def
shutdown
(
self
):
'''
Stop the server and free up the port
'''
# First call superclass shutdown()
HTTPServer
.
shutdown
(
self
)
# We also need to manually close the socket
self
.
socket
.
close
()
lms/djangoapps/django_comment_client/tests/mock_cs_server/test_mock_cs_server.py
0 → 100644
View file @
01e15c1e
import
unittest
import
threading
import
json
import
urllib2
from
mock_cs_server
import
MockCommentServiceServer
from
nose.plugins.skip
import
SkipTest
class
MockCommentServiceServerTest
(
unittest
.
TestCase
):
'''
A mock version of the Comment Service server that listens on a local
port and responds with pre-defined grade messages.
'''
def
setUp
(
self
):
# This is a test of the test setup,
# so it does not need to run as part of the unit test suite
# You can re-enable it by commenting out the line below
raise
SkipTest
# Create the server
server_port
=
4567
self
.
server_url
=
'http://127.0.0.1:
%
d'
%
server_port
# Start up the server and tell it that by default it should
# return this as its json response
self
.
expected_response
=
{
'username'
:
'user100'
,
'external_id'
:
'4'
}
self
.
server
=
MockCommentServiceServer
(
port_num
=
server_port
,
response
=
self
.
expected_response
)
# Start the server in a separate daemon thread
server_thread
=
threading
.
Thread
(
target
=
self
.
server
.
serve_forever
)
server_thread
.
daemon
=
True
server_thread
.
start
()
def
tearDown
(
self
):
# Stop the server, freeing up the port
self
.
server
.
shutdown
()
def
test_new_user_request
(
self
):
"""
Test the mock comment service using an example
of how you would create a new user
"""
# Send a request
values
=
{
'username'
:
u'user100'
,
'api_key'
:
'TEST_API_KEY'
,
'external_id'
:
'4'
,
'email'
:
u'user100@edx.org'
}
data
=
json
.
dumps
(
values
)
headers
=
{
'Content-Type'
:
'application/json'
,
'Content-Length'
:
len
(
data
)}
req
=
urllib2
.
Request
(
self
.
server_url
+
'/api/v1/users/4'
,
data
,
headers
)
# Send the request to the mock cs server
response
=
urllib2
.
urlopen
(
req
)
# Receive the reply from the mock cs server
response_dict
=
json
.
loads
(
response
.
read
())
# You should have received the response specified in the setup above
self
.
assertEqual
(
response_dict
,
self
.
expected_response
)
lms/djangoapps/django_comment_client/utils.py
View file @
01e15c1e
...
@@ -146,28 +146,16 @@ def sort_map_entries(category_map):
...
@@ -146,28 +146,16 @@ def sort_map_entries(category_map):
def
initialize_discussion_info
(
course
):
def
initialize_discussion_info
(
course
):
global
_DISCUSSIONINFO
global
_DISCUSSIONINFO
# only cache in-memory discussion information for 10 minutes
# this is because we need a short-term hack fix for
# mongo-backed courseware whereby new discussion modules can be added
# without LMS service restart
if
_DISCUSSIONINFO
[
course
.
id
]:
timestamp
=
_DISCUSSIONINFO
[
course
.
id
]
.
get
(
'timestamp'
,
datetime
.
now
())
age
=
datetime
.
now
()
-
timestamp
# expire every 5 minutes
if
age
.
seconds
<
300
:
return
course_id
=
course
.
id
course_id
=
course
.
id
discussion_id_map
=
{}
discussion_id_map
=
{}
unexpanded_category_map
=
defaultdict
(
list
)
unexpanded_category_map
=
defaultdict
(
list
)
# get all discussion models within this course_id
# get all discussion models within this course_id
all_modules
=
modulestore
()
.
get_items
([
'i4x'
,
course
.
location
.
org
,
course
.
location
.
course
,
'discussion'
,
None
],
course_id
=
course_id
)
all_modules
=
modulestore
()
.
get_items
([
'i4x'
,
course
.
location
.
org
,
course
.
location
.
course
,
'discussion'
,
None
],
course_id
=
course_id
)
for
module
in
all_modules
:
for
module
in
all_modules
:
skip_module
=
False
skip_module
=
False
...
...
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