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
4f7e09e9
Commit
4f7e09e9
authored
Mar 26, 2013
by
Brian Talbot
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into feature/btalbot/studio-tenderwidget
parents
7e56e98c
f70511eb
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
395 additions
and
110 deletions
+395
-110
cms/djangoapps/contentstore/features/course-settings.feature
+25
-0
cms/djangoapps/contentstore/features/course-settings.py
+163
-0
cms/djangoapps/contentstore/tests/test_contentstore.py
+31
-3
cms/static/js/base.js
+4
-2
cms/static/js/models/settings/course_details.js
+3
-0
cms/static/js/views/settings/main_settings_view.js
+6
-0
cms/static/js/views/validating_view.js
+14
-14
common/lib/capa/capa/templates/choicegroup.html
+4
-2
common/lib/xmodule/xmodule/course_module.py
+8
-4
common/lib/xmodule/xmodule/fields.py
+2
-0
common/lib/xmodule/xmodule/modulestore/mongo.py
+3
-0
common/lib/xmodule/xmodule/tests/test_course_module.py
+7
-6
lms/djangoapps/courseware/features/problems.feature
+4
-0
lms/djangoapps/courseware/features/problems.py
+114
-15
lms/templates/accounts_login.html
+7
-64
No files found.
cms/djangoapps/contentstore/features/course-settings.feature
0 → 100644
View file @
4f7e09e9
Feature
:
Course Settings
As a course author, I want to be able to configure my course settings.
Scenario
:
User can set course dates
Given
I have opened a new course in Studio
When
I select Schedule and Details
And
I set course dates
Then
I see the set dates on refresh
Scenario
:
User can clear previously set course dates (except start date)
Given
I have set course dates
And
I clear all the dates except start
Then
I see cleared dates on refresh
Scenario
:
User cannot clear the course start date
Given
I have set course dates
And
I clear the course start date
Then
I receive a warning about course start date
And
The previously set start date is shown on refresh
Scenario
:
User can correct the course start date warning
Given
I have tried to clear the course start
And
I have entered a new course start date
Then
The warning about course start date goes away
And
My new course start date is shown on refresh
cms/djangoapps/contentstore/features/course-settings.py
0 → 100644
View file @
4f7e09e9
from
lettuce
import
world
,
step
from
common
import
*
from
terrain.steps
import
reload_the_page
from
selenium.webdriver.common.keys
import
Keys
import
time
from
nose.tools
import
assert_true
,
assert_false
,
assert_equal
COURSE_START_DATE_CSS
=
"#course-start-date"
COURSE_END_DATE_CSS
=
"#course-end-date"
ENROLLMENT_START_DATE_CSS
=
"#course-enrollment-start-date"
ENROLLMENT_END_DATE_CSS
=
"#course-enrollment-end-date"
COURSE_START_TIME_CSS
=
"#course-start-time"
COURSE_END_TIME_CSS
=
"#course-end-time"
ENROLLMENT_START_TIME_CSS
=
"#course-enrollment-start-time"
ENROLLMENT_END_TIME_CSS
=
"#course-enrollment-end-time"
DUMMY_TIME
=
"3:30pm"
DEFAULT_TIME
=
"12:00am"
############### ACTIONS ####################
@step
(
'I select Schedule and Details$'
)
def
test_i_select_schedule_and_details
(
step
):
expand_icon_css
=
'li.nav-course-settings i.icon-expand'
if
world
.
browser
.
is_element_present_by_css
(
expand_icon_css
):
css_click
(
expand_icon_css
)
link_css
=
'li.nav-course-settings-schedule a'
css_click
(
link_css
)
@step
(
'I have set course dates$'
)
def
test_i_have_set_course_dates
(
step
):
step
.
given
(
'I have opened a new course in Studio'
)
step
.
given
(
'I select Schedule and Details'
)
step
.
given
(
'And I set course dates'
)
@step
(
'And I set course dates$'
)
def
test_and_i_set_course_dates
(
step
):
set_date_or_time
(
COURSE_START_DATE_CSS
,
'12/20/2013'
)
set_date_or_time
(
COURSE_END_DATE_CSS
,
'12/26/2013'
)
set_date_or_time
(
ENROLLMENT_START_DATE_CSS
,
'12/1/2013'
)
set_date_or_time
(
ENROLLMENT_END_DATE_CSS
,
'12/10/2013'
)
set_date_or_time
(
COURSE_START_TIME_CSS
,
DUMMY_TIME
)
set_date_or_time
(
ENROLLMENT_END_TIME_CSS
,
DUMMY_TIME
)
pause
()
@step
(
'Then I see the set dates on refresh$'
)
def
test_then_i_see_the_set_dates_on_refresh
(
step
):
reload_the_page
(
step
)
verify_date_or_time
(
COURSE_START_DATE_CSS
,
'12/20/2013'
)
verify_date_or_time
(
COURSE_END_DATE_CSS
,
'12/26/2013'
)
verify_date_or_time
(
ENROLLMENT_START_DATE_CSS
,
'12/01/2013'
)
verify_date_or_time
(
ENROLLMENT_END_DATE_CSS
,
'12/10/2013'
)
verify_date_or_time
(
COURSE_START_TIME_CSS
,
DUMMY_TIME
)
# Unset times get set to 12 AM once the corresponding date has been set.
verify_date_or_time
(
COURSE_END_TIME_CSS
,
DEFAULT_TIME
)
verify_date_or_time
(
ENROLLMENT_START_TIME_CSS
,
DEFAULT_TIME
)
verify_date_or_time
(
ENROLLMENT_END_TIME_CSS
,
DUMMY_TIME
)
@step
(
'And I clear all the dates except start$'
)
def
test_and_i_clear_all_the_dates_except_start
(
step
):
set_date_or_time
(
COURSE_END_DATE_CSS
,
''
)
set_date_or_time
(
ENROLLMENT_START_DATE_CSS
,
''
)
set_date_or_time
(
ENROLLMENT_END_DATE_CSS
,
''
)
pause
()
@step
(
'Then I see cleared dates on refresh$'
)
def
test_then_i_see_cleared_dates_on_refresh
(
step
):
reload_the_page
(
step
)
verify_date_or_time
(
COURSE_END_DATE_CSS
,
''
)
verify_date_or_time
(
ENROLLMENT_START_DATE_CSS
,
''
)
verify_date_or_time
(
ENROLLMENT_END_DATE_CSS
,
''
)
verify_date_or_time
(
COURSE_END_TIME_CSS
,
''
)
verify_date_or_time
(
ENROLLMENT_START_TIME_CSS
,
''
)
verify_date_or_time
(
ENROLLMENT_END_TIME_CSS
,
''
)
# Verify course start date (required) and time still there
verify_date_or_time
(
COURSE_START_DATE_CSS
,
'12/20/2013'
)
verify_date_or_time
(
COURSE_START_TIME_CSS
,
DUMMY_TIME
)
@step
(
'I clear the course start date$'
)
def
test_i_clear_the_course_start_date
(
step
):
set_date_or_time
(
COURSE_START_DATE_CSS
,
''
)
@step
(
'I receive a warning about course start date$'
)
def
test_i_receive_a_warning_about_course_start_date
(
step
):
assert_css_with_text
(
'.message-error'
,
'The course must have an assigned start date.'
)
assert_true
(
'error'
in
css_find
(
COURSE_START_DATE_CSS
)
.
first
.
_element
.
get_attribute
(
'class'
))
assert_true
(
'error'
in
css_find
(
COURSE_START_TIME_CSS
)
.
first
.
_element
.
get_attribute
(
'class'
))
@step
(
'The previously set start date is shown on refresh$'
)
def
test_the_previously_set_start_date_is_shown_on_refresh
(
step
):
reload_the_page
(
step
)
verify_date_or_time
(
COURSE_START_DATE_CSS
,
'12/20/2013'
)
verify_date_or_time
(
COURSE_START_TIME_CSS
,
DUMMY_TIME
)
@step
(
'Given I have tried to clear the course start$'
)
def
test_i_have_tried_to_clear_the_course_start
(
step
):
step
.
given
(
"I have set course dates"
)
step
.
given
(
"I clear the course start date"
)
step
.
given
(
"I receive a warning about course start date"
)
@step
(
'I have entered a new course start date$'
)
def
test_i_have_entered_a_new_course_start_date
(
step
):
set_date_or_time
(
COURSE_START_DATE_CSS
,
'12/22/2013'
)
pause
()
@step
(
'The warning about course start date goes away$'
)
def
test_the_warning_about_course_start_date_goes_away
(
step
):
assert_equal
(
0
,
len
(
css_find
(
'.message-error'
)))
assert_false
(
'error'
in
css_find
(
COURSE_START_DATE_CSS
)
.
first
.
_element
.
get_attribute
(
'class'
))
assert_false
(
'error'
in
css_find
(
COURSE_START_TIME_CSS
)
.
first
.
_element
.
get_attribute
(
'class'
))
@step
(
'My new course start date is shown on refresh$'
)
def
test_my_new_course_start_date_is_shown_on_refresh
(
step
):
reload_the_page
(
step
)
verify_date_or_time
(
COURSE_START_DATE_CSS
,
'12/22/2013'
)
# Time should have stayed from before attempt to clear date.
verify_date_or_time
(
COURSE_START_TIME_CSS
,
DUMMY_TIME
)
############### HELPER METHODS ####################
def
set_date_or_time
(
css
,
date_or_time
):
"""
Sets date or time field.
"""
css_fill
(
css
,
date_or_time
)
e
=
css_find
(
css
)
.
first
# hit Enter to apply the changes
e
.
_element
.
send_keys
(
Keys
.
ENTER
)
def
verify_date_or_time
(
css
,
date_or_time
):
"""
Verifies date or time field.
"""
assert_equal
(
date_or_time
,
css_find
(
css
)
.
first
.
value
)
def
pause
():
"""
Must sleep briefly to allow last time save to finish,
else refresh of browser will fail.
"""
time
.
sleep
(
float
(
1
))
cms/djangoapps/contentstore/tests/test_contentstore.py
View file @
4f7e09e9
...
@@ -37,6 +37,14 @@ TEST_DATA_MODULESTORE = copy.deepcopy(settings.MODULESTORE)
...
@@ -37,6 +37,14 @@ TEST_DATA_MODULESTORE = copy.deepcopy(settings.MODULESTORE)
TEST_DATA_MODULESTORE
[
'default'
][
'OPTIONS'
][
'fs_root'
]
=
path
(
'common/test/data'
)
TEST_DATA_MODULESTORE
[
'default'
][
'OPTIONS'
][
'fs_root'
]
=
path
(
'common/test/data'
)
TEST_DATA_MODULESTORE
[
'direct'
][
'OPTIONS'
][
'fs_root'
]
=
path
(
'common/test/data'
)
TEST_DATA_MODULESTORE
[
'direct'
][
'OPTIONS'
][
'fs_root'
]
=
path
(
'common/test/data'
)
class
MongoCollectionFindWrapper
(
object
):
def
__init__
(
self
,
original
):
self
.
original
=
original
self
.
counter
=
0
def
find
(
self
,
query
,
*
args
,
**
kwargs
):
self
.
counter
=
self
.
counter
+
1
return
self
.
original
(
query
,
*
args
,
**
kwargs
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MODULESTORE
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MODULESTORE
)
class
ContentStoreToyCourseTest
(
ModuleStoreTestCase
):
class
ContentStoreToyCourseTest
(
ModuleStoreTestCase
):
...
@@ -145,8 +153,6 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -145,8 +153,6 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
# make sure the parent no longer points to the child object which was deleted
# make sure the parent no longer points to the child object which was deleted
self
.
assertFalse
(
sequential
.
location
.
url
()
in
chapter
.
children
)
self
.
assertFalse
(
sequential
.
location
.
url
()
in
chapter
.
children
)
def
test_about_overrides
(
self
):
def
test_about_overrides
(
self
):
'''
'''
This test case verifies that a course can use specialized override for about data, e.g. /about/Fall_2012/effort.html
This test case verifies that a course can use specialized override for about data, e.g. /about/Fall_2012/effort.html
...
@@ -205,7 +211,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -205,7 +211,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
new_loc
=
descriptor
.
location
.
_replace
(
org
=
'MITx'
,
course
=
'999'
)
new_loc
=
descriptor
.
location
.
_replace
(
org
=
'MITx'
,
course
=
'999'
)
print
"Checking {0} should now also be at {1}"
.
format
(
descriptor
.
location
.
url
(),
new_loc
.
url
())
print
"Checking {0} should now also be at {1}"
.
format
(
descriptor
.
location
.
url
(),
new_loc
.
url
())
resp
=
self
.
client
.
get
(
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
new_loc
.
url
()}))
resp
=
self
.
client
.
get
(
reverse
(
'edit_unit'
,
kwargs
=
{
'location'
:
new_loc
.
url
()}))
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
def
test_delete_course
(
self
):
def
test_delete_course
(
self
):
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
...
@@ -307,6 +313,28 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
...
@@ -307,6 +313,28 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
# note, we know the link it should be because that's what in the 'full' course in the test data
# note, we know the link it should be because that's what in the 'full' course in the test data
self
.
assertContains
(
resp
,
'/c4x/edX/full/asset/handouts_schematic_tutorial.pdf'
)
self
.
assertContains
(
resp
,
'/c4x/edX/full/asset/handouts_schematic_tutorial.pdf'
)
def
test_prefetch_children
(
self
):
import_from_xml
(
modulestore
(),
'common/test/data/'
,
[
'full'
])
module_store
=
modulestore
(
'direct'
)
location
=
CourseDescriptor
.
id_to_location
(
'edX/full/6.002_Spring_2012'
)
wrapper
=
MongoCollectionFindWrapper
(
module_store
.
collection
.
find
)
module_store
.
collection
.
find
=
wrapper
.
find
course
=
module_store
.
get_item
(
location
,
depth
=
2
)
# make sure we haven't done too many round trips to DB
# note we say 4 round trips here for 1) the course, 2 & 3) for the chapters and sequentials, and
# 4) because of the RT due to calculating the inherited metadata
self
.
assertEqual
(
wrapper
.
counter
,
4
)
# make sure we pre-fetched a known sequential which should be at depth=2
self
.
assertTrue
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'sequential'
,
'Administrivia_and_Circuit_Elements'
,
None
])
in
course
.
system
.
module_data
)
# make sure we don't have a specific vertical which should be at depth=3
self
.
assertFalse
(
Location
([
'i4x'
,
'edX'
,
'full'
,
'vertical'
,
'vertical_58'
,
None
])
in
course
.
system
.
module_data
)
def
test_export_course_with_unknown_metadata
(
self
):
def
test_export_course_with_unknown_metadata
(
self
):
module_store
=
modulestore
(
'direct'
)
module_store
=
modulestore
(
'direct'
)
content_store
=
contentstore
()
content_store
=
contentstore
()
...
...
cms/static/js/base.js
View file @
4f7e09e9
...
@@ -251,7 +251,7 @@ function getEdxTimeFromDateTimeVals(date_val, time_val, format) {
...
@@ -251,7 +251,7 @@ function getEdxTimeFromDateTimeVals(date_val, time_val, format) {
time_val
=
'00:00'
;
time_val
=
'00:00'
;
// Note, we are using date.js utility which has better parsing abilities than the built in JS date parsing
// Note, we are using date.js utility which has better parsing abilities than the built in JS date parsing
date
=
Date
.
parse
(
date_val
+
" "
+
time_val
);
var
date
=
Date
.
parse
(
date_val
+
" "
+
time_val
);
if
(
format
==
null
)
if
(
format
==
null
)
format
=
'yyyy-MM-ddTHH:mm'
;
format
=
'yyyy-MM-ddTHH:mm'
;
...
@@ -269,6 +269,7 @@ function getEdxTimeFromDateTimeInputs(date_id, time_id, format) {
...
@@ -269,6 +269,7 @@ function getEdxTimeFromDateTimeInputs(date_id, time_id, format) {
}
}
function
autosaveInput
(
e
)
{
function
autosaveInput
(
e
)
{
var
self
=
this
;
if
(
this
.
saveTimer
)
{
if
(
this
.
saveTimer
)
{
clearTimeout
(
this
.
saveTimer
);
clearTimeout
(
this
.
saveTimer
);
}
}
...
@@ -276,7 +277,7 @@ function autosaveInput(e) {
...
@@ -276,7 +277,7 @@ function autosaveInput(e) {
this
.
saveTimer
=
setTimeout
(
function
()
{
this
.
saveTimer
=
setTimeout
(
function
()
{
$changedInput
=
$
(
e
.
target
);
$changedInput
=
$
(
e
.
target
);
saveSubsection
();
saveSubsection
();
this
.
saveTimer
=
null
;
self
.
saveTimer
=
null
;
},
500
);
},
500
);
}
}
...
@@ -318,6 +319,7 @@ function saveSubsection() {
...
@@ -318,6 +319,7 @@ function saveSubsection() {
data
:
JSON
.
stringify
({
'id'
:
id
,
'metadata'
:
metadata
}),
data
:
JSON
.
stringify
({
'id'
:
id
,
'metadata'
:
metadata
}),
success
:
function
()
{
success
:
function
()
{
$spinner
.
delay
(
500
).
fadeOut
(
150
);
$spinner
.
delay
(
500
).
fadeOut
(
150
);
$changedInput
=
null
;
},
},
error
:
function
()
{
error
:
function
()
{
showToastMessage
(
'There has been an error while saving your changes.'
);
showToastMessage
(
'There has been an error while saving your changes.'
);
...
...
cms/static/js/models/settings/course_details.js
View file @
4f7e09e9
...
@@ -37,6 +37,9 @@ CMS.Models.Settings.CourseDetails = Backbone.Model.extend({
...
@@ -37,6 +37,9 @@ CMS.Models.Settings.CourseDetails = Backbone.Model.extend({
// Returns either nothing (no return call) so that validate works or an object of {field: errorstring} pairs
// Returns either nothing (no return call) so that validate works or an object of {field: errorstring} pairs
// A bit funny in that the video key validation is asynchronous; so, it won't stop the validation.
// A bit funny in that the video key validation is asynchronous; so, it won't stop the validation.
var
errors
=
{};
var
errors
=
{};
if
(
newattrs
.
start_date
===
null
)
{
errors
.
start_date
=
"The course must have an assigned start date."
;
}
if
(
newattrs
.
start_date
&&
newattrs
.
end_date
&&
newattrs
.
start_date
>=
newattrs
.
end_date
)
{
if
(
newattrs
.
start_date
&&
newattrs
.
end_date
&&
newattrs
.
start_date
>=
newattrs
.
end_date
)
{
errors
.
end_date
=
"The course end date cannot be before the course start date."
;
errors
.
end_date
=
"The course end date cannot be before the course start date."
;
}
}
...
...
cms/static/js/views/settings/main_settings_view.js
View file @
4f7e09e9
...
@@ -101,6 +101,12 @@ CMS.Views.Settings.Details = CMS.Views.ValidatingView.extend({
...
@@ -101,6 +101,12 @@ CMS.Views.Settings.Details = CMS.Views.ValidatingView.extend({
cacheModel
.
save
(
fieldName
,
newVal
);
cacheModel
.
save
(
fieldName
,
newVal
);
}
}
}
}
else
{
// Clear date (note that this clears the time as well, as date and time are linked).
// Note also that the validation logic prevents us from clearing the start date
// (start date is required by the back end).
cacheModel
.
save
(
fieldName
,
null
);
}
};
};
// instrument as date and time pickers
// instrument as date and time pickers
...
...
cms/static/js/views/validating_view.js
View file @
4f7e09e9
...
@@ -25,14 +25,7 @@ CMS.Views.ValidatingView = Backbone.View.extend({
...
@@ -25,14 +25,7 @@ CMS.Views.ValidatingView = Backbone.View.extend({
for
(
var
field
in
error
)
{
for
(
var
field
in
error
)
{
var
ele
=
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
field
]);
var
ele
=
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
field
]);
this
.
_cacheValidationErrors
.
push
(
ele
);
this
.
_cacheValidationErrors
.
push
(
ele
);
var
inputElements
=
'input, textarea'
;
this
.
getInputElements
(
ele
).
addClass
(
'error'
);
if
(
$
(
ele
).
is
(
inputElements
))
{
$
(
ele
).
addClass
(
'error'
);
}
else
{
// put error on the contained inputs
$
(
ele
).
find
(
inputElements
).
addClass
(
'error'
);
}
$
(
ele
).
parent
().
append
(
this
.
errorTemplate
({
message
:
error
[
field
]}));
$
(
ele
).
parent
().
append
(
this
.
errorTemplate
({
message
:
error
[
field
]}));
}
}
},
},
...
@@ -40,12 +33,8 @@ CMS.Views.ValidatingView = Backbone.View.extend({
...
@@ -40,12 +33,8 @@ CMS.Views.ValidatingView = Backbone.View.extend({
clearValidationErrors
:
function
()
{
clearValidationErrors
:
function
()
{
// error is object w/ fields and error strings
// error is object w/ fields and error strings
while
(
this
.
_cacheValidationErrors
.
length
>
0
)
{
while
(
this
.
_cacheValidationErrors
.
length
>
0
)
{
var
ele
=
this
.
_cacheValidationErrors
.
pop
();
var
ele
=
this
.
_cacheValidationErrors
.
pop
();
if
(
$
(
ele
).
is
(
'div'
))
{
this
.
getInputElements
(
ele
).
removeClass
(
'error'
);
// put error on the contained inputs
$
(
ele
).
find
(
'input, textarea'
).
removeClass
(
'error'
);
}
else
$
(
ele
).
removeClass
(
'error'
);
$
(
ele
).
nextAll
(
'.message-error'
).
remove
();
$
(
ele
).
nextAll
(
'.message-error'
).
remove
();
}
}
},
},
...
@@ -68,5 +57,16 @@ CMS.Views.ValidatingView = Backbone.View.extend({
...
@@ -68,5 +57,16 @@ CMS.Views.ValidatingView = Backbone.View.extend({
},
},
inputUnfocus
:
function
(
event
)
{
inputUnfocus
:
function
(
event
)
{
$
(
"label[for='"
+
event
.
currentTarget
.
id
+
"']"
).
removeClass
(
"is-focused"
);
$
(
"label[for='"
+
event
.
currentTarget
.
id
+
"']"
).
removeClass
(
"is-focused"
);
},
getInputElements
:
function
(
ele
)
{
var
inputElements
=
'input, textarea'
;
if
(
$
(
ele
).
is
(
inputElements
))
{
return
$
(
ele
);
}
else
{
// put error on the contained inputs
return
$
(
ele
).
find
(
inputElements
);
}
}
}
});
});
common/lib/capa/capa/templates/choicegroup.html
View file @
4f7e09e9
...
@@ -17,7 +17,7 @@
...
@@ -17,7 +17,7 @@
% for choice_id, choice_description in choices:
% for choice_id, choice_description in choices:
<label
for=
"input_${id}_${choice_id}"
<label
for=
"input_${id}_${choice_id}"
%
if
input_type =
=
'
radio
'
and
choice_id
in
value:
%
if
input_type =
=
'
radio
'
and
choice_id
=
=
value:
<%
<%
if
status =
=
'
correct
'
:
if
status =
=
'
correct
'
:
correctness =
'correct'
correctness =
'correct'
...
@@ -32,8 +32,10 @@
...
@@ -32,8 +32,10 @@
% endif
% endif
>
>
<input
type=
"${input_type}"
name=
"input_${id}${name_array_suffix}"
id=
"input_${id}_${choice_id}"
value=
"${choice_id}"
<input
type=
"${input_type}"
name=
"input_${id}${name_array_suffix}"
id=
"input_${id}_${choice_id}"
value=
"${choice_id}"
%
if
choice_id
in
value:
%
if
input_type =
=
'
radio
'
and
choice_id =
=
value:
checked=
"true"
checked=
"true"
%
elif
input_type
!=
'
radio
'
and
choice_id
in
value:
checked=
"true"
%
endif
%
endif
/>
${choice_description}
</label>
/>
${choice_description}
</label>
...
...
common/lib/xmodule/xmodule/course_module.py
View file @
4f7e09e9
...
@@ -7,6 +7,8 @@ import requests
...
@@ -7,6 +7,8 @@ import requests
import
time
import
time
from
datetime
import
datetime
from
datetime
import
datetime
import
dateutil.parser
from
xmodule.modulestore
import
Location
from
xmodule.modulestore
import
Location
from
xmodule.seq_module
import
SequenceDescriptor
,
SequenceModule
from
xmodule.seq_module
import
SequenceDescriptor
,
SequenceModule
from
xmodule.timeparse
import
parse_time
from
xmodule.timeparse
import
parse_time
...
@@ -150,7 +152,7 @@ class CourseFields(object):
...
@@ -150,7 +152,7 @@ class CourseFields(object):
enrollment_end
=
Date
(
help
=
"Date that enrollment for this class is closed"
,
scope
=
Scope
.
settings
)
enrollment_end
=
Date
(
help
=
"Date that enrollment for this class is closed"
,
scope
=
Scope
.
settings
)
start
=
Date
(
help
=
"Start time when this module is visible"
,
scope
=
Scope
.
settings
)
start
=
Date
(
help
=
"Start time when this module is visible"
,
scope
=
Scope
.
settings
)
end
=
Date
(
help
=
"Date that this class ends"
,
scope
=
Scope
.
settings
)
end
=
Date
(
help
=
"Date that this class ends"
,
scope
=
Scope
.
settings
)
advertised_start
=
String
OrDate
(
help
=
"Date that this course is advertised to start"
,
scope
=
Scope
.
settings
)
advertised_start
=
String
(
help
=
"Date that this course is advertised to start"
,
scope
=
Scope
.
settings
)
grading_policy
=
Object
(
help
=
"Grading policy definition for this class"
,
scope
=
Scope
.
content
)
grading_policy
=
Object
(
help
=
"Grading policy definition for this class"
,
scope
=
Scope
.
content
)
show_calculator
=
Boolean
(
help
=
"Whether to show the calculator in this course"
,
default
=
False
,
scope
=
Scope
.
settings
)
show_calculator
=
Boolean
(
help
=
"Whether to show the calculator in this course"
,
default
=
False
,
scope
=
Scope
.
settings
)
display_name
=
String
(
help
=
"Display name for this module"
,
scope
=
Scope
.
settings
)
display_name
=
String
(
help
=
"Display name for this module"
,
scope
=
Scope
.
settings
)
...
@@ -537,10 +539,12 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
...
@@ -537,10 +539,12 @@ class CourseDescriptor(CourseFields, SequenceDescriptor):
announcement
=
self
.
announcement
announcement
=
self
.
announcement
if
announcement
is
not
None
:
if
announcement
is
not
None
:
announcement
=
to_datetime
(
announcement
)
announcement
=
to_datetime
(
announcement
)
if
self
.
advertised_start
is
None
or
isinstance
(
self
.
advertised_start
,
basestring
):
try
:
start
=
dateutil
.
parser
.
parse
(
self
.
advertised_start
)
except
(
ValueError
,
AttributeError
):
start
=
to_datetime
(
self
.
start
)
start
=
to_datetime
(
self
.
start
)
else
:
start
=
to_datetime
(
self
.
advertised_start
)
now
=
to_datetime
(
time
.
gmtime
())
now
=
to_datetime
(
time
.
gmtime
())
return
announcement
,
start
,
now
return
announcement
,
start
,
now
...
...
common/lib/xmodule/xmodule/fields.py
View file @
4f7e09e9
...
@@ -23,6 +23,8 @@ class Date(ModelType):
...
@@ -23,6 +23,8 @@ class Date(ModelType):
"""
"""
if
field
is
None
:
if
field
is
None
:
return
field
return
field
elif
field
is
""
:
return
None
elif
isinstance
(
field
,
basestring
):
elif
isinstance
(
field
,
basestring
):
d
=
dateutil
.
parser
.
parse
(
field
)
d
=
dateutil
.
parser
.
parse
(
field
)
return
d
.
utctimetuple
()
return
d
.
utctimetuple
()
...
...
common/lib/xmodule/xmodule/modulestore/mongo.py
View file @
4f7e09e9
...
@@ -366,6 +366,9 @@ class MongoModuleStore(ModuleStoreBase):
...
@@ -366,6 +366,9 @@ class MongoModuleStore(ModuleStoreBase):
children
.
extend
(
item
.
get
(
'definition'
,
{})
.
get
(
'children'
,
[]))
children
.
extend
(
item
.
get
(
'definition'
,
{})
.
get
(
'children'
,
[]))
data
[
Location
(
item
[
'location'
])]
=
item
data
[
Location
(
item
[
'location'
])]
=
item
if
depth
==
0
:
break
;
# Load all children by id. See
# Load all children by id. See
# http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24or
# http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24or
# for or-query syntax
# for or-query syntax
...
...
common/lib/xmodule/xmodule/tests/test_course_module.py
View file @
4f7e09e9
...
@@ -89,18 +89,19 @@ class IsNewCourseTestCase(unittest.TestCase):
...
@@ -89,18 +89,19 @@ class IsNewCourseTestCase(unittest.TestCase):
((
day2
,
None
,
None
),
(
day1
,
None
,
None
),
self
.
assertLess
),
((
day2
,
None
,
None
),
(
day1
,
None
,
None
),
self
.
assertLess
),
((
day1
,
None
,
None
),
(
day1
,
None
,
None
),
self
.
assertEqual
),
((
day1
,
None
,
None
),
(
day1
,
None
,
None
),
self
.
assertEqual
),
# Non-parseable advertised starts are ignored in preference
# Non-parseable advertised starts are ignored in preference to actual starts
# to actual starts
((
day2
,
None
,
"Spring"
),
(
day1
,
None
,
"Fall"
),
self
.
assertLess
),
((
day2
,
None
,
"Spring 2013"
),
(
day1
,
None
,
"Fall 2012"
),
self
.
assertLess
),
((
day1
,
None
,
"Spring"
),
(
day1
,
None
,
"Fall"
),
self
.
assertEqual
),
((
day1
,
None
,
"Spring 2013"
),
(
day1
,
None
,
"Fall 2012"
),
self
.
assertEqual
),
# Partially parsable advertised starts should take priority over start dates
((
day2
,
None
,
"October 2013"
),
(
day2
,
None
,
"October 2012"
),
self
.
assertLess
),
((
day2
,
None
,
"October 2013"
),
(
day1
,
None
,
"October 2013"
),
self
.
assertEqual
),
# Parseable advertised starts take priority over start dates
# Parseable advertised starts take priority over start dates
((
day1
,
None
,
day2
),
(
day1
,
None
,
day1
),
self
.
assertLess
),
((
day1
,
None
,
day2
),
(
day1
,
None
,
day1
),
self
.
assertLess
),
((
day2
,
None
,
day2
),
(
day1
,
None
,
day2
),
self
.
assertEqual
),
((
day2
,
None
,
day2
),
(
day1
,
None
,
day2
),
self
.
assertEqual
),
]
]
data
=
[]
for
a
,
b
,
assertion
in
dates
:
for
a
,
b
,
assertion
in
dates
:
a_score
=
self
.
get_dummy_course
(
start
=
a
[
0
],
announcement
=
a
[
1
],
advertised_start
=
a
[
2
])
.
sorting_score
a_score
=
self
.
get_dummy_course
(
start
=
a
[
0
],
announcement
=
a
[
1
],
advertised_start
=
a
[
2
])
.
sorting_score
b_score
=
self
.
get_dummy_course
(
start
=
b
[
0
],
announcement
=
b
[
1
],
advertised_start
=
b
[
2
])
.
sorting_score
b_score
=
self
.
get_dummy_course
(
start
=
b
[
0
],
announcement
=
b
[
1
],
advertised_start
=
b
[
2
])
.
sorting_score
...
...
lms/djangoapps/courseware/features/problems.feature
View file @
4f7e09e9
...
@@ -8,6 +8,7 @@ Feature: Answer problems
...
@@ -8,6 +8,7 @@ Feature: Answer problems
And
I am viewing a
"<ProblemType>"
problem
And
I am viewing a
"<ProblemType>"
problem
When
I answer a
"<ProblemType>"
problem
"correctly"
When
I answer a
"<ProblemType>"
problem
"correctly"
Then
My
"<ProblemType>"
answer is marked
"correct"
Then
My
"<ProblemType>"
answer is marked
"correct"
And
The
"<ProblemType>"
problem displays a
"correct"
answer
Examples
:
Examples
:
|
ProblemType
|
|
ProblemType
|
...
@@ -25,6 +26,7 @@ Feature: Answer problems
...
@@ -25,6 +26,7 @@ Feature: Answer problems
And
I am viewing a
"<ProblemType>"
problem
And
I am viewing a
"<ProblemType>"
problem
When
I answer a
"<ProblemType>"
problem
"incorrectly"
When
I answer a
"<ProblemType>"
problem
"incorrectly"
Then
My
"<ProblemType>"
answer is marked
"incorrect"
Then
My
"<ProblemType>"
answer is marked
"incorrect"
And
The
"<ProblemType>"
problem displays a
"incorrect"
answer
Examples
:
Examples
:
|
ProblemType
|
|
ProblemType
|
...
@@ -41,6 +43,7 @@ Feature: Answer problems
...
@@ -41,6 +43,7 @@ Feature: Answer problems
Given
I am viewing a
"<ProblemType>"
problem
Given
I am viewing a
"<ProblemType>"
problem
When
I check a problem
When
I check a problem
Then
My
"<ProblemType>"
answer is marked
"incorrect"
Then
My
"<ProblemType>"
answer is marked
"incorrect"
And
The
"<ProblemType>"
problem displays a
"blank"
answer
Examples
:
Examples
:
|
ProblemType
|
|
ProblemType
|
...
@@ -58,6 +61,7 @@ Feature: Answer problems
...
@@ -58,6 +61,7 @@ Feature: Answer problems
And
I answer a
"<ProblemType>"
problem
"<Correctness>ly"
And
I answer a
"<ProblemType>"
problem
"<Correctness>ly"
When
I reset the problem
When
I reset the problem
Then
My
"<ProblemType>"
answer is marked
"unanswered"
Then
My
"<ProblemType>"
answer is marked
"unanswered"
And
The
"<ProblemType>"
problem displays a
"blank"
answer
Examples
:
Examples
:
|
ProblemType
|
Correctness
|
|
ProblemType
|
Correctness
|
...
...
lms/djangoapps/courseware/features/problems.py
View file @
4f7e09e9
'''
Steps for problem.feature lettuce tests
'''
from
lettuce
import
world
,
step
from
lettuce
import
world
,
step
from
lettuce.django
import
django_url
from
lettuce.django
import
django_url
import
random
import
random
import
textwrap
import
textwrap
import
time
from
common
import
i_am_registered_for_the_course
,
\
from
common
import
i_am_registered_for_the_course
,
TEST_SECTION_NAME
,
section_location
TEST_SECTION_NAME
,
section_location
from
capa.tests.response_xml_factory
import
OptionResponseXMLFactory
,
\
from
capa.tests.response_xml_factory
import
OptionResponseXMLFactory
,
\
ChoiceResponseXMLFactory
,
MultipleChoiceResponseXMLFactory
,
\
ChoiceResponseXMLFactory
,
MultipleChoiceResponseXMLFactory
,
\
StringResponseXMLFactory
,
NumericalResponseXMLFactory
,
\
StringResponseXMLFactory
,
NumericalResponseXMLFactory
,
\
...
@@ -26,7 +31,7 @@ PROBLEM_FACTORY_DICT = {
...
@@ -26,7 +31,7 @@ PROBLEM_FACTORY_DICT = {
'kwargs'
:
{
'kwargs'
:
{
'question_text'
:
'The correct answer is Choice 3'
,
'question_text'
:
'The correct answer is Choice 3'
,
'choices'
:
[
False
,
False
,
True
,
False
],
'choices'
:
[
False
,
False
,
True
,
False
],
'choice_names'
:
[
'choice_
1'
,
'choice_2'
,
'choice_3'
,
'choice_4
'
]}},
'choice_names'
:
[
'choice_
0'
,
'choice_1'
,
'choice_2'
,
'choice_3
'
]}},
'checkbox'
:
{
'checkbox'
:
{
'factory'
:
ChoiceResponseXMLFactory
(),
'factory'
:
ChoiceResponseXMLFactory
(),
...
@@ -88,6 +93,9 @@ PROBLEM_FACTORY_DICT = {
...
@@ -88,6 +93,9 @@ PROBLEM_FACTORY_DICT = {
def
add_problem_to_course
(
course
,
problem_type
):
def
add_problem_to_course
(
course
,
problem_type
):
'''
Add a problem to the course we have created using factories.
'''
assert
(
problem_type
in
PROBLEM_FACTORY_DICT
)
assert
(
problem_type
in
PROBLEM_FACTORY_DICT
)
...
@@ -98,11 +106,12 @@ def add_problem_to_course(course, problem_type):
...
@@ -98,11 +106,12 @@ def add_problem_to_course(course, problem_type):
# Create a problem item using our generated XML
# Create a problem item using our generated XML
# We set rerandomize=always in the metadata so that the "Reset" button
# We set rerandomize=always in the metadata so that the "Reset" button
# will appear.
# will appear.
problem_item
=
world
.
ItemFactory
.
create
(
parent_location
=
section_location
(
course
),
template_name
=
"i4x://edx/templates/problem/Blank_Common_Problem"
template
=
"i4x://edx/templates/problem/Blank_Common_Problem"
,
world
.
ItemFactory
.
create
(
parent_location
=
section_location
(
course
),
display_name
=
str
(
problem_type
),
template
=
template_name
,
data
=
problem_xml
,
display_name
=
str
(
problem_type
),
metadata
=
{
'rerandomize'
:
'always'
})
data
=
problem_xml
,
metadata
=
{
'rerandomize'
:
'always'
})
@step
(
u'I am viewing a "([^"]*)" problem'
)
@step
(
u'I am viewing a "([^"]*)" problem'
)
...
@@ -152,9 +161,9 @@ def answer_problem(step, problem_type, correctness):
...
@@ -152,9 +161,9 @@ def answer_problem(step, problem_type, correctness):
elif
problem_type
==
"multiple choice"
:
elif
problem_type
==
"multiple choice"
:
if
correctness
==
'correct'
:
if
correctness
==
'correct'
:
inputfield
(
'multiple choice'
,
choice
=
'choice_3'
)
.
check
()
else
:
inputfield
(
'multiple choice'
,
choice
=
'choice_2'
)
.
check
()
inputfield
(
'multiple choice'
,
choice
=
'choice_2'
)
.
check
()
else
:
inputfield
(
'multiple choice'
,
choice
=
'choice_1'
)
.
check
()
elif
problem_type
==
"checkbox"
:
elif
problem_type
==
"checkbox"
:
if
correctness
==
'correct'
:
if
correctness
==
'correct'
:
...
@@ -164,11 +173,13 @@ def answer_problem(step, problem_type, correctness):
...
@@ -164,11 +173,13 @@ def answer_problem(step, problem_type, correctness):
inputfield
(
'checkbox'
,
choice
=
'choice_3'
)
.
check
()
inputfield
(
'checkbox'
,
choice
=
'choice_3'
)
.
check
()
elif
problem_type
==
'string'
:
elif
problem_type
==
'string'
:
textvalue
=
'correct string'
if
correctness
==
'correct'
else
'incorrect'
textvalue
=
'correct string'
if
correctness
==
'correct'
\
else
'incorrect'
inputfield
(
'string'
)
.
fill
(
textvalue
)
inputfield
(
'string'
)
.
fill
(
textvalue
)
elif
problem_type
==
'numerical'
:
elif
problem_type
==
'numerical'
:
textvalue
=
"pi + 1"
if
correctness
==
'correct'
else
str
(
random
.
randint
(
-
2
,
2
))
textvalue
=
"pi + 1"
if
correctness
==
'correct'
\
else
str
(
random
.
randint
(
-
2
,
2
))
inputfield
(
'numerical'
)
.
fill
(
textvalue
)
inputfield
(
'numerical'
)
.
fill
(
textvalue
)
elif
problem_type
==
'formula'
:
elif
problem_type
==
'formula'
:
...
@@ -203,6 +214,67 @@ def answer_problem(step, problem_type, correctness):
...
@@ -203,6 +214,67 @@ def answer_problem(step, problem_type, correctness):
check_problem
(
step
)
check_problem
(
step
)
@step
(
u'The "([^"]*)" problem displays a "([^"]*)" answer'
)
def
assert_problem_has_answer
(
step
,
problem_type
,
answer_class
):
'''
Assert that the problem is displaying a particular answer.
These correspond to the same correct/incorrect
answers we set in answer_problem()
We can also check that a problem has been left blank
by setting answer_class='blank'
'''
assert
answer_class
in
[
'correct'
,
'incorrect'
,
'blank'
]
if
problem_type
==
"drop down"
:
if
answer_class
==
'blank'
:
assert
world
.
browser
.
is_element_not_present_by_css
(
'option[selected="true"]'
)
else
:
actual
=
world
.
browser
.
find_by_css
(
'option[selected="true"]'
)
.
value
expected
=
'Option 2'
if
answer_class
==
'correct'
else
'Option 3'
assert
actual
==
expected
elif
problem_type
==
"multiple choice"
:
if
answer_class
==
'correct'
:
assert_checked
(
'multiple choice'
,
[
'choice_2'
])
elif
answer_class
==
'incorrect'
:
assert_checked
(
'multiple choice'
,
[
'choice_1'
])
else
:
assert_checked
(
'multiple choice'
,
[])
elif
problem_type
==
"checkbox"
:
if
answer_class
==
'correct'
:
assert_checked
(
'checkbox'
,
[
'choice_0'
,
'choice_2'
])
elif
answer_class
==
'incorrect'
:
assert_checked
(
'checkbox'
,
[
'choice_3'
])
else
:
assert_checked
(
'checkbox'
,
[])
elif
problem_type
==
'string'
:
if
answer_class
==
'blank'
:
expected
=
''
else
:
expected
=
'correct string'
if
answer_class
==
'correct'
\
else
'incorrect'
assert_textfield
(
'string'
,
expected
)
elif
problem_type
==
'formula'
:
if
answer_class
==
'blank'
:
expected
=
''
else
:
expected
=
"x^2+2*x+y"
if
answer_class
==
'correct'
else
'x^2'
assert_textfield
(
'formula'
,
expected
)
else
:
# The other response types use random data,
# which would be difficult to check
# We trade input value coverage in the other tests for
# input type coverage in this test.
pass
@step
(
u'I check a problem'
)
@step
(
u'I check a problem'
)
def
check_problem
(
step
):
def
check_problem
(
step
):
world
.
css_click
(
"input.check"
)
world
.
css_click
(
"input.check"
)
...
@@ -227,7 +299,7 @@ CORRECTNESS_SELECTORS = {
...
@@ -227,7 +299,7 @@ CORRECTNESS_SELECTORS = {
'string'
:
[
'div.correct'
],
'string'
:
[
'div.correct'
],
'numerical'
:
[
'div.correct'
],
'numerical'
:
[
'div.correct'
],
'formula'
:
[
'div.correct'
],
'formula'
:
[
'div.correct'
],
'script'
:
[
'div.correct'
],
'script'
:
[
'div.correct'
],
'code'
:
[
'span.correct'
]},
'code'
:
[
'span.correct'
]},
'incorrect'
:
{
'drop down'
:
[
'span.incorrect'
],
'incorrect'
:
{
'drop down'
:
[
'span.incorrect'
],
...
@@ -247,12 +319,14 @@ CORRECTNESS_SELECTORS = {
...
@@ -247,12 +319,14 @@ CORRECTNESS_SELECTORS = {
'numerical'
:
[
'div.unanswered'
],
'numerical'
:
[
'div.unanswered'
],
'formula'
:
[
'div.unanswered'
],
'formula'
:
[
'div.unanswered'
],
'script'
:
[
'div.unanswered'
],
'script'
:
[
'div.unanswered'
],
'code'
:
[
'span.unanswered'
]
}}
'code'
:
[
'span.unanswered'
]}}
@step
(
u'My "([^"]*)" answer is marked "([^"]*)"'
)
@step
(
u'My "([^"]*)" answer is marked "([^"]*)"'
)
def
assert_answer_mark
(
step
,
problem_type
,
correctness
):
def
assert_answer_mark
(
step
,
problem_type
,
correctness
):
""" Assert that the expected answer mark is visible for a given problem type.
"""
Assert that the expected answer mark is visible
for a given problem type.
*problem_type* is a string identifying the type of problem (e.g. 'drop down')
*problem_type* is a string identifying the type of problem (e.g. 'drop down')
*correctness* is in ['correct', 'incorrect', 'unanswered']
*correctness* is in ['correct', 'incorrect', 'unanswered']
...
@@ -274,6 +348,7 @@ def assert_answer_mark(step, problem_type, correctness):
...
@@ -274,6 +348,7 @@ def assert_answer_mark(step, problem_type, correctness):
# Expect that we found the expected selector
# Expect that we found the expected selector
assert
(
has_expected
)
assert
(
has_expected
)
def
inputfield
(
problem_type
,
choice
=
None
,
input_num
=
1
):
def
inputfield
(
problem_type
,
choice
=
None
,
input_num
=
1
):
""" Return the <input> element for *problem_type*.
""" Return the <input> element for *problem_type*.
For example, if problem_type is 'string', return
For example, if problem_type is 'string', return
...
@@ -289,8 +364,32 @@ def inputfield(problem_type, choice=None, input_num=1):
...
@@ -289,8 +364,32 @@ def inputfield(problem_type, choice=None, input_num=1):
base
=
"_choice_"
if
problem_type
==
"multiple choice"
else
"_"
base
=
"_choice_"
if
problem_type
==
"multiple choice"
else
"_"
sel
=
sel
+
base
+
str
(
choice
)
sel
=
sel
+
base
+
str
(
choice
)
# If the input element doesn't exist, fail immediately
# If the input element doesn't exist, fail immediately
assert
(
world
.
browser
.
is_element_present_by_css
(
sel
,
wait_time
=
4
))
assert
(
world
.
browser
.
is_element_present_by_css
(
sel
,
wait_time
=
4
))
# Retrieve the input element
# Retrieve the input element
return
world
.
browser
.
find_by_css
(
sel
)
return
world
.
browser
.
find_by_css
(
sel
)
def
assert_checked
(
problem_type
,
choices
):
'''
Assert that choice names given in *choices* are the only
ones checked.
Works for both radio and checkbox problems
'''
all_choices
=
[
'choice_0'
,
'choice_1'
,
'choice_2'
,
'choice_3'
]
for
this_choice
in
all_choices
:
element
=
inputfield
(
problem_type
,
choice
=
this_choice
)
if
this_choice
in
choices
:
assert
element
.
checked
else
:
assert
not
element
.
checked
def
assert_textfield
(
problem_type
,
expected_text
,
input_num
=
1
):
element
=
inputfield
(
problem_type
,
input_num
=
input_num
)
assert
element
.
value
==
expected_text
lms/templates/accounts_login.html
View file @
4f7e09e9
...
@@ -10,7 +10,7 @@
...
@@ -10,7 +10,7 @@
left
:
0
;
left
:
0
;
margin
:
100px
auto
;
margin
:
100px
auto
;
top
:
0
;
top
:
0
;
z-index
:
200
;
height
:
500px
;
}
}
.login-box
input
[
type
=
submit
]
{
.login-box
input
[
type
=
submit
]
{
...
@@ -18,75 +18,18 @@
...
@@ -18,75 +18,18 @@
height
:
auto
!important
;
height
:
auto
!important
;
}
}
#lean_overlay
{
display
:
block
;
position
:
fixed
;
left
:
0px
;
top
:
0px
;
z-index
:
100
;
width
:
100%
;
height
:
100%
;
}
</style>
</style>
</
%
block>
</
%
block>
<section
id=
"login-modal"
class=
"modal login-modal login-box"
>
<section
class=
'login-box'
></section>
<div
class=
"inner-wrapper"
>
<header>
<h2>
Log In
</h2>
<hr>
</header>
<form
id=
"login_form"
class=
"login_form"
method=
"post"
data-remote=
"true"
action=
"/login"
>
<label>
E-mail
</label>
<input
name=
"email"
type=
"email"
>
<label>
Password
</label>
<input
name=
"password"
type=
"password"
>
<label
class=
"remember-me"
>
<input
name=
"remember"
type=
"checkbox"
value=
"true"
>
Remember me
</label>
<div
class=
"submit"
>
<input
name=
"submit"
type=
"submit"
value=
"Access My Courses"
>
</div>
</form>
<section
class=
"login-extra"
>
<p>
<span>
Not enrolled?
<a
href=
"#signup-modal"
class=
"close-login"
rel=
"leanModal"
>
Sign up.
</a></span>
<a
href=
"#forgot-password-modal"
rel=
"leanModal"
class=
"pwd-reset"
>
Forgot password?
</a>
</p>
% if settings.MITX_FEATURES.get('AUTH_USE_OPENID'):
<p>
<a
href=
"${MITX_ROOT_URL}/openid/login/"
>
login via openid
</a>
</p>
% endif
</section>
<div
class=
"close-modal"
>
<div
class=
"inner"
>
<p>
✕
</p>
</div>
</div>
</div>
</section>
<script
type=
"text/javascript"
>
<script
type=
"text/javascript"
>
(
function
()
{
(
function
()
{
$
(
document
).
delegate
(
'#login_form'
,
'ajax:success'
,
function
(
data
,
json
,
xhr
)
{
$
(
document
).
ready
(
if
(
json
.
success
)
{
function
()
{
next
=
getParameterByName
(
'next'
);
// show dialog
if
(
next
)
{
$
(
'#login'
).
click
();
location
.
href
=
next
;
}
else
{
location
.
href
=
"${reverse('dashboard')}"
;
}
}
else
{
if
(
$
(
'#login_error'
).
length
==
0
)
{
$
(
'#login_form'
).
prepend
(
'<div id="login_error" class="modal-form-error"></div>'
);
}
$
(
'#login_error'
).
html
(
json
.
value
).
stop
().
css
(
"display"
,
"block"
);
}
}
}
);
);
})(
this
)
})(
this
)
</script>
</script>
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