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
80cf4d6e
Commit
80cf4d6e
authored
Jul 20, 2015
by
Max Rothman
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #8969 from edx/release-merge-test
Release merge test
parents
4c0b00f4
babf2420
Hide whitespace changes
Inline
Side-by-side
Showing
33 changed files
with
281 additions
and
197 deletions
+281
-197
cms/djangoapps/contentstore/management/commands/export_convert_format.py
+3
-2
cms/djangoapps/contentstore/management/commands/tests/test_export_convert_format.py
+4
-3
cms/djangoapps/contentstore/views/tests/test_import_export.py
+30
-5
cms/envs/bok_choy.py
+1
-0
cms/envs/test.py
+1
-0
common/djangoapps/course_modes/tests/test_views.py
+16
-0
common/djangoapps/course_modes/views.py
+2
-1
common/djangoapps/student/tests/tests.py
+13
-0
common/djangoapps/student/views.py
+1
-0
common/lib/xmodule/xmodule/x_module.py
+6
-0
common/static/js/utils/rwd_header.js
+0
-94
common/test/acceptance/pages/lms/dashboard.py
+0
-14
common/test/acceptance/tests/lms/test_lms.py
+0
-6
lms/djangoapps/commerce/tests/test_views.py
+12
-0
lms/djangoapps/commerce/views.py
+2
-1
lms/djangoapps/courseware/tests/test_module_render.py
+23
-2
lms/djangoapps/verify_student/tests/test_views.py
+11
-0
lms/djangoapps/verify_student/views.py
+1
-0
lms/envs/aws.py
+5
-0
lms/envs/common.py
+0
-6
lms/static/sass/elements/_controls.scss
+13
-0
lms/static/sass/multicourse/_dashboard.scss
+23
-6
lms/static/sass/shared/_header.scss
+2
-10
lms/templates/commerce/checkout_receipt.html
+0
-1
lms/templates/dashboard.html
+9
-6
lms/templates/navigation-edx.html
+15
-29
lms/templates/verify_student/incourse_reverify.html
+0
-1
lms/templates/verify_student/pay_and_verify.html
+0
-1
lms/templates/verify_student/reverify.html
+0
-1
openedx/core/djangoapps/content/course_overviews/migrations/0004_default_lowest_passing_grade_to_None.py
+50
-0
openedx/core/djangoapps/content/course_overviews/models.py
+12
-2
openedx/core/djangoapps/content/course_overviews/tests.py
+15
-0
openedx/core/lib/extract_tar.py
+11
-6
No files found.
cms/djangoapps/contentstore/management/commands/export_convert_format.py
View file @
80cf4d6e
...
@@ -7,6 +7,7 @@ Sample invocation: ./manage.py export_convert_format mycourse.tar.gz ~/newformat
...
@@ -7,6 +7,7 @@ Sample invocation: ./manage.py export_convert_format mycourse.tar.gz ~/newformat
import
os
import
os
from
path
import
path
from
path
import
path
from
django.core.management.base
import
BaseCommand
,
CommandError
from
django.core.management.base
import
BaseCommand
,
CommandError
from
django.conf
import
settings
from
tempfile
import
mkdtemp
from
tempfile
import
mkdtemp
import
tarfile
import
tarfile
...
@@ -32,8 +33,8 @@ class Command(BaseCommand):
...
@@ -32,8 +33,8 @@ class Command(BaseCommand):
output_path
=
args
[
1
]
output_path
=
args
[
1
]
# Create temp directories to extract the source and create the target archive.
# Create temp directories to extract the source and create the target archive.
temp_source_dir
=
mkdtemp
()
temp_source_dir
=
mkdtemp
(
dir
=
settings
.
DATA_DIR
)
temp_target_dir
=
mkdtemp
()
temp_target_dir
=
mkdtemp
(
dir
=
settings
.
DATA_DIR
)
try
:
try
:
extract_source
(
source_archive
,
temp_source_dir
)
extract_source
(
source_archive
,
temp_source_dir
)
...
...
cms/djangoapps/contentstore/management/commands/tests/test_export_convert_format.py
View file @
80cf4d6e
...
@@ -3,6 +3,7 @@ Test for export_convert_format.
...
@@ -3,6 +3,7 @@ Test for export_convert_format.
"""
"""
from
unittest
import
TestCase
from
unittest
import
TestCase
from
django.core.management
import
call_command
,
CommandError
from
django.core.management
import
call_command
,
CommandError
from
django.conf
import
settings
from
tempfile
import
mkdtemp
from
tempfile
import
mkdtemp
import
shutil
import
shutil
from
path
import
path
from
path
import
path
...
@@ -18,7 +19,7 @@ class ConvertExportFormat(TestCase):
...
@@ -18,7 +19,7 @@ class ConvertExportFormat(TestCase):
""" Common setup. """
""" Common setup. """
super
(
ConvertExportFormat
,
self
)
.
setUp
()
super
(
ConvertExportFormat
,
self
)
.
setUp
()
self
.
temp_dir
=
mkdtemp
()
self
.
temp_dir
=
mkdtemp
(
dir
=
settings
.
DATA_DIR
)
self
.
addCleanup
(
shutil
.
rmtree
,
self
.
temp_dir
)
self
.
addCleanup
(
shutil
.
rmtree
,
self
.
temp_dir
)
self
.
data_dir
=
path
(
__file__
)
.
realpath
()
.
parent
/
'data'
self
.
data_dir
=
path
(
__file__
)
.
realpath
()
.
parent
/
'data'
self
.
version0
=
self
.
data_dir
/
"Version0_drafts.tar.gz"
self
.
version0
=
self
.
data_dir
/
"Version0_drafts.tar.gz"
...
@@ -52,8 +53,8 @@ class ConvertExportFormat(TestCase):
...
@@ -52,8 +53,8 @@ class ConvertExportFormat(TestCase):
"""
"""
Helper function for determining if 2 archives are equal.
Helper function for determining if 2 archives are equal.
"""
"""
temp_dir_1
=
mkdtemp
()
temp_dir_1
=
mkdtemp
(
dir
=
settings
.
DATA_DIR
)
temp_dir_2
=
mkdtemp
()
temp_dir_2
=
mkdtemp
(
dir
=
settings
.
DATA_DIR
)
try
:
try
:
extract_source
(
file1
,
temp_dir_1
)
extract_source
(
file1
,
temp_dir_1
)
extract_source
(
file2
,
temp_dir_2
)
extract_source
(
file2
,
temp_dir_2
)
...
...
cms/djangoapps/contentstore/views/tests/test_import_export.py
View file @
80cf4d6e
...
@@ -209,6 +209,19 @@ class ImportTestCase(CourseTestCase):
...
@@ -209,6 +209,19 @@ class ImportTestCase(CourseTestCase):
return
outside_tar
return
outside_tar
def
_edx_platform_tar
(
self
):
"""
Tarfile with file that extracts to edx-platform directory.
Extracting this tarfile in directory <dir> will also put its contents
directly in <dir> (rather than <dir/tarname>).
"""
outside_tar
=
self
.
unsafe_common_dir
/
"unsafe_file.tar.gz"
with
tarfile
.
open
(
outside_tar
,
"w:gz"
)
as
tar
:
tar
.
addfile
(
tarfile
.
TarInfo
(
os
.
path
.
join
(
os
.
path
.
abspath
(
"."
),
"a_file"
)))
return
outside_tar
def
test_unsafe_tar
(
self
):
def
test_unsafe_tar
(
self
):
"""
"""
Check that safety measure work.
Check that safety measure work.
...
@@ -233,6 +246,12 @@ class ImportTestCase(CourseTestCase):
...
@@ -233,6 +246,12 @@ class ImportTestCase(CourseTestCase):
try_tar
(
self
.
_symlink_tar
())
try_tar
(
self
.
_symlink_tar
())
try_tar
(
self
.
_outside_tar
())
try_tar
(
self
.
_outside_tar
())
try_tar
(
self
.
_outside_tar2
())
try_tar
(
self
.
_outside_tar2
())
try_tar
(
self
.
_edx_platform_tar
())
# test trying to open a tar outside of the normal data directory
with
self
.
settings
(
DATA_DIR
=
'/not/the/data/dir'
):
try_tar
(
self
.
_edx_platform_tar
())
# Check that `import_status` returns the appropriate stage (i.e.,
# Check that `import_status` returns the appropriate stage (i.e.,
# either 3, indicating all previous steps are completed, or 0,
# either 3, indicating all previous steps are completed, or 0,
# indicating no upload in progress)
# indicating no upload in progress)
...
@@ -294,13 +313,19 @@ class ImportTestCase(CourseTestCase):
...
@@ -294,13 +313,19 @@ class ImportTestCase(CourseTestCase):
self
.
assertIn
(
test_block3
.
url_name
,
children
)
self
.
assertIn
(
test_block3
.
url_name
,
children
)
self
.
assertIn
(
test_block4
.
url_name
,
children
)
self
.
assertIn
(
test_block4
.
url_name
,
children
)
extract_dir
=
path
(
tempfile
.
mkdtemp
())
extract_dir
=
path
(
tempfile
.
mkdtemp
(
dir
=
settings
.
DATA_DIR
))
# the extract_dir needs to be passed as a relative dir to
# import_library_from_xml
extract_dir_relative
=
path
.
relpath
(
extract_dir
,
settings
.
DATA_DIR
)
try
:
try
:
tar
=
tarfile
.
open
(
path
(
TEST_DATA_DIR
)
/
'imports'
/
'library.HhJfPD.tar.gz'
)
with
tarfile
.
open
(
path
(
TEST_DATA_DIR
)
/
'imports'
/
'library.HhJfPD.tar.gz'
)
as
tar
:
safetar_extractall
(
tar
,
extract_dir
)
safetar_extractall
(
tar
,
extract_dir
)
library_items
=
import_library_from_xml
(
library_items
=
import_library_from_xml
(
self
.
store
,
self
.
user
.
id
,
self
.
store
,
settings
.
GITHUB_REPO_ROOT
,
[
extract_dir
/
'library'
],
self
.
user
.
id
,
settings
.
GITHUB_REPO_ROOT
,
[
extract_dir_relative
/
'library'
],
load_error_modules
=
False
,
load_error_modules
=
False
,
static_content_store
=
contentstore
(),
static_content_store
=
contentstore
(),
target_id
=
lib_key
target_id
=
lib_key
...
...
cms/envs/bok_choy.py
View file @
80cf4d6e
...
@@ -39,6 +39,7 @@ INSTALLED_APPS += ('django_extensions',)
...
@@ -39,6 +39,7 @@ INSTALLED_APPS += ('django_extensions',)
TEST_ROOT
=
REPO_ROOT
/
"test_root"
# pylint: disable=no-value-for-parameter
TEST_ROOT
=
REPO_ROOT
/
"test_root"
# pylint: disable=no-value-for-parameter
GITHUB_REPO_ROOT
=
(
TEST_ROOT
/
"data"
)
.
abspath
()
GITHUB_REPO_ROOT
=
(
TEST_ROOT
/
"data"
)
.
abspath
()
LOG_DIR
=
(
TEST_ROOT
/
"log"
)
.
abspath
()
LOG_DIR
=
(
TEST_ROOT
/
"log"
)
.
abspath
()
DATA_DIR
=
TEST_ROOT
/
"data"
# Configure modulestore to use the test folder within the repo
# Configure modulestore to use the test folder within the repo
update_module_store_settings
(
update_module_store_settings
(
...
...
cms/envs/test.py
View file @
80cf4d6e
...
@@ -65,6 +65,7 @@ TEST_ROOT = path('test_root')
...
@@ -65,6 +65,7 @@ TEST_ROOT = path('test_root')
STATIC_ROOT
=
TEST_ROOT
/
"staticfiles"
STATIC_ROOT
=
TEST_ROOT
/
"staticfiles"
GITHUB_REPO_ROOT
=
TEST_ROOT
/
"data"
GITHUB_REPO_ROOT
=
TEST_ROOT
/
"data"
DATA_DIR
=
TEST_ROOT
/
"data"
COMMON_TEST_DATA_ROOT
=
COMMON_ROOT
/
"test"
/
"data"
COMMON_TEST_DATA_ROOT
=
COMMON_ROOT
/
"test"
/
"data"
# For testing "push to lms"
# For testing "push to lms"
...
...
common/djangoapps/course_modes/tests/test_views.py
View file @
80cf4d6e
...
@@ -325,6 +325,22 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase):
...
@@ -325,6 +325,22 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase):
self
.
assertEquals
(
course_modes
,
expected_modes
)
self
.
assertEquals
(
course_modes
,
expected_modes
)
@unittest.skipUnless
(
settings
.
ROOT_URLCONF
==
'lms.urls'
,
'Test only valid in lms'
)
@patch.dict
(
settings
.
FEATURES
,
{
"IS_EDX_DOMAIN"
:
True
})
def
test_hide_nav
(
self
):
# Create the course modes
for
mode
in
[
"honor"
,
"verified"
]:
CourseModeFactory
(
mode_slug
=
mode
,
course_id
=
self
.
course
.
id
)
# Load the track selection page
url
=
reverse
(
'course_modes_choose'
,
args
=
[
unicode
(
self
.
course
.
id
)])
response
=
self
.
client
.
get
(
url
)
# Verify that the header navigation links are hidden for the edx.org version
self
.
assertNotContains
(
response
,
"How it Works"
)
self
.
assertNotContains
(
response
,
"Find courses"
)
self
.
assertNotContains
(
response
,
"Schools & Partners"
)
@unittest.skipUnless
(
settings
.
ROOT_URLCONF
==
'lms.urls'
,
'Test only valid in lms'
)
@unittest.skipUnless
(
settings
.
ROOT_URLCONF
==
'lms.urls'
,
'Test only valid in lms'
)
class
TrackSelectionEmbargoTest
(
UrlResetMixin
,
ModuleStoreTestCase
):
class
TrackSelectionEmbargoTest
(
UrlResetMixin
,
ModuleStoreTestCase
):
...
...
common/djangoapps/course_modes/views.py
View file @
80cf4d6e
...
@@ -119,7 +119,8 @@ class ChooseModeView(View):
...
@@ -119,7 +119,8 @@ class ChooseModeView(View):
"course_num"
:
course
.
display_number_with_default
,
"course_num"
:
course
.
display_number_with_default
,
"chosen_price"
:
chosen_price
,
"chosen_price"
:
chosen_price
,
"error"
:
error
,
"error"
:
error
,
"responsive"
:
True
"responsive"
:
True
,
"nav_hidden"
:
True
,
}
}
if
"verified"
in
modes
:
if
"verified"
in
modes
:
context
[
"suggested_prices"
]
=
[
context
[
"suggested_prices"
]
=
[
...
...
common/djangoapps/student/tests/tests.py
View file @
80cf4d6e
...
@@ -529,6 +529,19 @@ class DashboardTest(ModuleStoreTestCase):
...
@@ -529,6 +529,19 @@ class DashboardTest(ModuleStoreTestCase):
response_3
=
self
.
client
.
get
(
reverse
(
'dashboard'
))
response_3
=
self
.
client
.
get
(
reverse
(
'dashboard'
))
self
.
assertEquals
(
response_3
.
status_code
,
200
)
self
.
assertEquals
(
response_3
.
status_code
,
200
)
@unittest.skipUnless
(
settings
.
ROOT_URLCONF
==
'lms.urls'
,
'Test only valid in lms'
)
@patch.dict
(
settings
.
FEATURES
,
{
"IS_EDX_DOMAIN"
:
True
})
def
test_dashboard_header_nav_has_find_courses
(
self
):
self
.
client
.
login
(
username
=
"jack"
,
password
=
"test"
)
response
=
self
.
client
.
get
(
reverse
(
"dashboard"
))
# "Find courses" is shown in the side panel
self
.
assertContains
(
response
,
"Find courses"
)
# But other links are hidden in the navigation
self
.
assertNotContains
(
response
,
"How it Works"
)
self
.
assertNotContains
(
response
,
"Schools & Partners"
)
class
UserSettingsEventTestMixin
(
EventTestMixin
):
class
UserSettingsEventTestMixin
(
EventTestMixin
):
"""
"""
...
...
common/djangoapps/student/views.py
View file @
80cf4d6e
...
@@ -694,6 +694,7 @@ def dashboard(request):
...
@@ -694,6 +694,7 @@ def dashboard(request):
'order_history_list'
:
order_history_list
,
'order_history_list'
:
order_history_list
,
'courses_requirements_not_met'
:
courses_requirements_not_met
,
'courses_requirements_not_met'
:
courses_requirements_not_met
,
'ccx_membership_triplets'
:
ccx_membership_triplets
,
'ccx_membership_triplets'
:
ccx_membership_triplets
,
'nav_hidden'
:
True
,
}
}
return
render_to_response
(
'dashboard.html'
,
context
)
return
render_to_response
(
'dashboard.html'
,
context
)
...
...
common/lib/xmodule/xmodule/x_module.py
View file @
80cf4d6e
...
@@ -591,6 +591,12 @@ class XModuleMixin(XModuleFields, XBlock):
...
@@ -591,6 +591,12 @@ class XModuleMixin(XModuleFields, XBlock):
if
field
.
scope
.
user
==
UserScope
.
ONE
:
if
field
.
scope
.
user
==
UserScope
.
ONE
:
field
.
_del_cached_value
(
self
)
# pylint: disable=protected-access
field
.
_del_cached_value
(
self
)
# pylint: disable=protected-access
# not the most elegant way of doing this, but if we're removing
# a field from the module's field_data_cache, we should also
# remove it from its _dirty_fields
# pylint: disable=protected-access
if
field
in
self
.
_dirty_fields
:
del
self
.
_dirty_fields
[
field
]
# Set the new xmodule_runtime and field_data (which are user-specific)
# Set the new xmodule_runtime and field_data (which are user-specific)
self
.
xmodule_runtime
=
xmodule_runtime
self
.
xmodule_runtime
=
xmodule_runtime
...
...
common/static/js/utils/rwd_header.js
deleted
100644 → 0
View file @
4c0b00f4
/**
* Adds rwd classes and click handlers.
*/
(
function
(
$
)
{
'use strict'
;
var
rwd
=
(
function
()
{
var
_fn
=
{
header
:
'header.global-new'
,
resultsUrl
:
'course-search'
,
init
:
function
()
{
_fn
.
$header
=
$
(
_fn
.
header
);
_fn
.
$footer
=
$
(
_fn
.
footer
);
_fn
.
$navContainer
=
_fn
.
$header
.
find
(
'.nav-container'
);
_fn
.
$globalNav
=
_fn
.
$header
.
find
(
'.nav-global'
);
_fn
.
add
.
elements
();
_fn
.
add
.
classes
();
_fn
.
eventHandlers
.
init
();
},
add
:
{
classes
:
function
()
{
// Add any RWD-specific classes
_fn
.
$header
.
addClass
(
'rwd'
);
},
elements
:
function
()
{
_fn
.
add
.
burger
();
_fn
.
add
.
registerLink
();
},
burger
:
function
()
{
_fn
.
$navContainer
.
prepend
([
'<a href="#" class="mobile-menu-button" aria-label="menu">'
,
'<i class="icon fa fa-bars" aria-hidden="true"></i>'
,
'</a>'
].
join
(
''
));
},
registerLink
:
function
()
{
var
$register
=
_fn
.
$header
.
find
(
'.cta-register'
),
$li
=
{},
$a
=
{},
count
=
0
;
// Add if register link is shown
if
(
$register
.
length
>
0
)
{
count
=
_fn
.
$globalNav
.
find
(
'li'
).
length
+
1
;
// Create new li
$li
=
$
(
'<li/>'
);
$li
.
addClass
(
'desktop-hide nav-global-0'
+
count
);
// Clone register link and remove classes
$a
=
$register
.
clone
();
$a
.
removeClass
();
// append to DOM
$a
.
appendTo
(
$li
);
_fn
.
$globalNav
.
append
(
$li
);
}
}
},
eventHandlers
:
{
init
:
function
()
{
_fn
.
eventHandlers
.
click
();
},
click
:
function
()
{
// Toggle menu
_fn
.
$header
.
on
(
'click'
,
'.mobile-menu-button'
,
_fn
.
toggleMenu
);
}
},
toggleMenu
:
function
(
event
)
{
event
.
preventDefault
();
_fn
.
$globalNav
.
toggleClass
(
'show'
);
}
};
return
{
init
:
_fn
.
init
};
})();
rwd
.
init
();
})(
jQuery
);
common/test/acceptance/pages/lms/dashboard.py
View file @
80cf4d6e
...
@@ -48,20 +48,6 @@ class DashboardPage(PageObject):
...
@@ -48,20 +48,6 @@ class DashboardPage(PageObject):
return
self
.
q
(
css
=
'h3.course-title > a'
)
.
map
(
_get_course_name
)
.
results
return
self
.
q
(
css
=
'h3.course-title > a'
)
.
map
(
_get_course_name
)
.
results
@property
def
sidebar_menu_title
(
self
):
"""
Return the title value for sidebar menu.
"""
return
self
.
q
(
css
=
'.user-info span.title'
)
.
text
[
0
]
@property
def
sidebar_menu_description
(
self
):
"""
Return the description text for sidebar menu.
"""
return
self
.
q
(
css
=
'.user-info span.copy'
)
.
text
[
0
]
def
get_enrollment_mode
(
self
,
course_name
):
def
get_enrollment_mode
(
self
,
course_name
):
"""Get the enrollment mode for a given course on the dashboard.
"""Get the enrollment mode for a given course on the dashboard.
...
...
common/test/acceptance/tests/lms/test_lms.py
View file @
80cf4d6e
...
@@ -267,12 +267,6 @@ class RegisterFromCombinedPageTest(UniqueCourseTest):
...
@@ -267,12 +267,6 @@ class RegisterFromCombinedPageTest(UniqueCourseTest):
course_names
=
self
.
dashboard_page
.
wait_for_page
()
.
available_courses
course_names
=
self
.
dashboard_page
.
wait_for_page
()
.
available_courses
self
.
assertIn
(
self
.
course_info
[
"display_name"
],
course_names
)
self
.
assertIn
(
self
.
course_info
[
"display_name"
],
course_names
)
self
.
assertEqual
(
"want to change your account settings?"
,
self
.
dashboard_page
.
sidebar_menu_title
.
lower
())
self
.
assertEqual
(
"click the arrow next to your username above."
,
self
.
dashboard_page
.
sidebar_menu_description
.
lower
()
)
def
test_register_failure
(
self
):
def
test_register_failure
(
self
):
# Navigate to the registration page
# Navigate to the registration page
self
.
register_page
.
visit
()
self
.
register_page
.
visit
()
...
...
lms/djangoapps/commerce/tests/test_views.py
View file @
80cf4d6e
...
@@ -4,6 +4,7 @@ from uuid import uuid4
...
@@ -4,6 +4,7 @@ from uuid import uuid4
from
nose.plugins.attrib
import
attr
from
nose.plugins.attrib
import
attr
import
ddt
import
ddt
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.test
import
TestCase
from
django.test
import
TestCase
import
mock
import
mock
...
@@ -84,3 +85,14 @@ class ReceiptViewTests(UserMixin, TestCase):
...
@@ -84,3 +85,14 @@ class ReceiptViewTests(UserMixin, TestCase):
system_message
=
"A system error occurred while processing your payment"
system_message
=
"A system error occurred while processing your payment"
self
.
assertRegexpMatches
(
response
.
content
,
user_message
if
is_user_message_expected
else
system_message
)
self
.
assertRegexpMatches
(
response
.
content
,
user_message
if
is_user_message_expected
else
system_message
)
self
.
assertNotRegexpMatches
(
response
.
content
,
user_message
if
not
is_user_message_expected
else
system_message
)
self
.
assertNotRegexpMatches
(
response
.
content
,
user_message
if
not
is_user_message_expected
else
system_message
)
@mock.patch.dict
(
settings
.
FEATURES
,
{
"IS_EDX_DOMAIN"
:
True
})
def
test_hide_nav_header
(
self
):
self
.
_login
()
post_data
=
{
'decision'
:
'ACCEPT'
,
'reason_code'
:
'200'
,
'signed_field_names'
:
'dummy'
}
response
=
self
.
post_to_receipt_page
(
post_data
)
# Verify that the header navigation links are hidden for the edx.org version
self
.
assertNotContains
(
response
,
"How it Works"
)
self
.
assertNotContains
(
response
,
"Find courses"
)
self
.
assertNotContains
(
response
,
"Schools & Partners"
)
lms/djangoapps/commerce/views.py
View file @
80cf4d6e
...
@@ -66,6 +66,7 @@ def checkout_receipt(request):
...
@@ -66,6 +66,7 @@ def checkout_receipt(request):
'error_text'
:
error_text
,
'error_text'
:
error_text
,
'for_help_text'
:
for_help_text
,
'for_help_text'
:
for_help_text
,
'payment_support_email'
:
payment_support_email
,
'payment_support_email'
:
payment_support_email
,
'username'
:
request
.
user
.
username
'username'
:
request
.
user
.
username
,
'nav_hidden'
:
True
,
}
}
return
render_to_response
(
'commerce/checkout_receipt.html'
,
context
)
return
render_to_response
(
'commerce/checkout_receipt.html'
,
context
)
lms/djangoapps/courseware/tests/test_module_render.py
View file @
80cf4d6e
...
@@ -1360,23 +1360,44 @@ class TestRebindModule(TestSubmittingProblems):
...
@@ -1360,23 +1360,44 @@ class TestRebindModule(TestSubmittingProblems):
super
(
TestRebindModule
,
self
)
.
setUp
()
super
(
TestRebindModule
,
self
)
.
setUp
()
self
.
homework
=
self
.
add_graded_section_to_course
(
'homework'
)
self
.
homework
=
self
.
add_graded_section_to_course
(
'homework'
)
self
.
lti
=
ItemFactory
.
create
(
category
=
'lti'
,
parent
=
self
.
homework
)
self
.
lti
=
ItemFactory
.
create
(
category
=
'lti'
,
parent
=
self
.
homework
)
self
.
problem
=
ItemFactory
.
create
(
category
=
'problem'
,
parent
=
self
.
homework
)
self
.
user
=
UserFactory
.
create
()
self
.
user
=
UserFactory
.
create
()
self
.
anon_user
=
AnonymousUser
()
self
.
anon_user
=
AnonymousUser
()
def
get_module_for_user
(
self
,
user
):
def
get_module_for_user
(
self
,
user
,
item
=
None
):
"""Helper function to get useful module at self.location in self.course_id for user"""
"""Helper function to get useful module at self.location in self.course_id for user"""
mock_request
=
MagicMock
()
mock_request
=
MagicMock
()
mock_request
.
user
=
user
mock_request
.
user
=
user
field_data_cache
=
FieldDataCache
.
cache_for_descriptor_descendents
(
field_data_cache
=
FieldDataCache
.
cache_for_descriptor_descendents
(
self
.
course
.
id
,
user
,
self
.
course
,
depth
=
2
)
self
.
course
.
id
,
user
,
self
.
course
,
depth
=
2
)
if
item
is
None
:
item
=
self
.
lti
return
render
.
get_module
(
# pylint: disable=protected-access
return
render
.
get_module
(
# pylint: disable=protected-access
user
,
user
,
mock_request
,
mock_request
,
self
.
lti
.
location
,
item
.
location
,
field_data_cache
,
field_data_cache
,
)
.
_xmodule
)
.
_xmodule
def
test_rebind_module_to_new_users
(
self
):
module
=
self
.
get_module_for_user
(
self
.
user
,
self
.
problem
)
# Bind the module to another student, which will remove "correct_map"
# from the module's _field_data_cache and _dirty_fields.
user2
=
UserFactory
.
create
()
module
.
descriptor
.
bind_for_student
(
module
.
system
,
user2
.
id
)
# XBlock's save method assumes that if a field is in _dirty_fields,
# then it's also in _field_data_cache. If this assumption
# doesn't hold, then we get an error trying to bind this module
# to a third student, since we've removed "correct_map" from
# _field_data cache, but not _dirty_fields, when we bound
# this module to the second student. (TNL-2640)
user3
=
UserFactory
.
create
()
module
.
descriptor
.
bind_for_student
(
module
.
system
,
user3
.
id
)
def
test_rebind_noauth_module_to_user_not_anonymous
(
self
):
def
test_rebind_noauth_module_to_user_not_anonymous
(
self
):
"""
"""
Tests that an exception is thrown when rebind_noauth_module_to_user is run from a
Tests that an exception is thrown when rebind_noauth_module_to_user is run from a
...
...
lms/djangoapps/verify_student/tests/test_views.py
View file @
80cf4d6e
...
@@ -237,6 +237,17 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase):
...
@@ -237,6 +237,17 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase):
)
)
self
.
_assert_redirects_to_dashboard
(
response
)
self
.
_assert_redirects_to_dashboard
(
response
)
@patch.dict
(
settings
.
FEATURES
,
{
"IS_EDX_DOMAIN"
:
True
})
def
test_pay_and_verify_hides_header_nav
(
self
):
course
=
self
.
_create_course
(
"verified"
)
self
.
_enroll
(
course
.
id
,
"verified"
)
response
=
self
.
_get_page
(
'verify_student_start_flow'
,
course
.
id
)
# Verify that the header navigation links are hidden for the edx.org version
self
.
assertNotContains
(
response
,
"How it Works"
)
self
.
assertNotContains
(
response
,
"Find courses"
)
self
.
assertNotContains
(
response
,
"Schools & Partners"
)
def
test_verify_now
(
self
):
def
test_verify_now
(
self
):
# We've already paid, and now we're trying to verify
# We've already paid, and now we're trying to verify
course
=
self
.
_create_course
(
"verified"
)
course
=
self
.
_create_course
(
"verified"
)
...
...
lms/djangoapps/verify_student/views.py
View file @
80cf4d6e
...
@@ -376,6 +376,7 @@ class PayAndVerifyView(View):
...
@@ -376,6 +376,7 @@ class PayAndVerifyView(View):
'already_verified'
:
already_verified
,
'already_verified'
:
already_verified
,
'verification_good_until'
:
verification_good_until
,
'verification_good_until'
:
verification_good_until
,
'capture_sound'
:
staticfiles_storage
.
url
(
"audio/camera_capture.wav"
),
'capture_sound'
:
staticfiles_storage
.
url
(
"audio/camera_capture.wav"
),
'nav_hidden'
:
True
,
}
}
return
render_to_response
(
"verify_student/pay_and_verify.html"
,
context
)
return
render_to_response
(
"verify_student/pay_and_verify.html"
,
context
)
...
...
lms/envs/aws.py
View file @
80cf4d6e
...
@@ -678,3 +678,8 @@ if FEATURES.get('ENABLE_LTI_PROVIDER'):
...
@@ -678,3 +678,8 @@ if FEATURES.get('ENABLE_LTI_PROVIDER'):
##################### Credit Provider help link ####################
##################### Credit Provider help link ####################
CREDIT_HELP_LINK_URL
=
ENV_TOKENS
.
get
(
'CREDIT_HELP_LINK_URL'
,
CREDIT_HELP_LINK_URL
)
CREDIT_HELP_LINK_URL
=
ENV_TOKENS
.
get
(
'CREDIT_HELP_LINK_URL'
,
CREDIT_HELP_LINK_URL
)
#### JWT configuration ####
JWT_ISSUER
=
ENV_TOKENS
.
get
(
'JWT_ISSUER'
,
JWT_ISSUER
)
JWT_EXPIRATION
=
ENV_TOKENS
.
get
(
'JWT_EXPIRATION'
,
JWT_EXPIRATION
)
lms/envs/common.py
View file @
80cf4d6e
...
@@ -1247,7 +1247,6 @@ dashboard_js = (
...
@@ -1247,7 +1247,6 @@ dashboard_js = (
)
)
dashboard_search_js
=
[
'js/search/dashboard/main.js'
]
dashboard_search_js
=
[
'js/search/dashboard/main.js'
]
discussion_js
=
sorted
(
rooted_glob
(
COMMON_ROOT
/
'static'
,
'coffee/src/discussion/**/*.js'
))
discussion_js
=
sorted
(
rooted_glob
(
COMMON_ROOT
/
'static'
,
'coffee/src/discussion/**/*.js'
))
rwd_header_js
=
sorted
(
rooted_glob
(
PROJECT_ROOT
/
'static'
,
'js/utils/rwd_header.js'
))
staff_grading_js
=
sorted
(
rooted_glob
(
PROJECT_ROOT
/
'static'
,
'coffee/src/staff_grading/**/*.js'
))
staff_grading_js
=
sorted
(
rooted_glob
(
PROJECT_ROOT
/
'static'
,
'coffee/src/staff_grading/**/*.js'
))
open_ended_js
=
sorted
(
rooted_glob
(
PROJECT_ROOT
/
'static'
,
'coffee/src/open_ended/**/*.js'
))
open_ended_js
=
sorted
(
rooted_glob
(
PROJECT_ROOT
/
'static'
,
'coffee/src/open_ended/**/*.js'
))
notes_js
=
sorted
(
rooted_glob
(
PROJECT_ROOT
/
'static'
,
'coffee/src/notes/**/*.js'
))
notes_js
=
sorted
(
rooted_glob
(
PROJECT_ROOT
/
'static'
,
'coffee/src/notes/**/*.js'
))
...
@@ -1260,7 +1259,6 @@ instructor_dash_js = (
...
@@ -1260,7 +1259,6 @@ instructor_dash_js = (
# These are not courseware, so they do not need many of the courseware-specific
# These are not courseware, so they do not need many of the courseware-specific
# JavaScript modules.
# JavaScript modules.
student_account_js
=
[
student_account_js
=
[
'js/utils/rwd_header.js'
,
'js/utils/edx.utils.validate.js'
,
'js/utils/edx.utils.validate.js'
,
'js/form.ext.js'
,
'js/form.ext.js'
,
'js/my_courses_dropdown.js'
,
'js/my_courses_dropdown.js'
,
...
@@ -1549,10 +1547,6 @@ PIPELINE_JS = {
...
@@ -1549,10 +1547,6 @@ PIPELINE_JS = {
'source_filenames'
:
dashboard_search_js
,
'source_filenames'
:
dashboard_search_js
,
'output_filename'
:
'js/dashboard-search.js'
,
'output_filename'
:
'js/dashboard-search.js'
,
},
},
'rwd_header'
:
{
'source_filenames'
:
rwd_header_js
,
'output_filename'
:
'js/rwd_header.js'
},
'student_account'
:
{
'student_account'
:
{
'source_filenames'
:
student_account_js
,
'source_filenames'
:
student_account_js
,
'output_filename'
:
'js/student_account.js'
'output_filename'
:
'js/student_account.js'
...
...
lms/static/sass/elements/_controls.scss
View file @
80cf4d6e
...
@@ -352,6 +352,19 @@
...
@@ -352,6 +352,19 @@
}
}
}
}
%btn-pl-elevated-alt
{
@extend
%btn-pl-default-base
;
box-shadow
:
0
3px
0
0
$gray-l4
;
border
:
1px
solid
$gray-l4
;
&
:hover
{
box-shadow
:
0
3px
0
0
$action-primary-bg
;
border
:
1px
solid
$action-primary-bg
;
background-color
:
lighten
(
$action-primary-bg
,
20%
);
color
:
$white
;
}
}
// ====================
// ====================
// application: canned actions
// application: canned actions
...
...
lms/static/sass/multicourse/_dashboard.scss
View file @
80cf4d6e
...
@@ -15,14 +15,31 @@
...
@@ -15,14 +15,31 @@
@include
clearfix
();
@include
clearfix
();
padding
:
(
$baseline
*
2
)
0
$baseline
0
;
padding
:
(
$baseline
*
2
)
0
$baseline
0
;
.wrapper-find-courses
{
@include
float
(
right
);
@include
margin-left
(
flex-gutter
());
width
:
flex-grid
(
3
);
margin-top
:
(
$baseline
*
2
);
border-top
:
3px
solid
$blue
;
padding
:
$baseline
0
;
.copy
{
@extend
%t-copy-sub1
;
}
.btn-find-courses
{
@extend
%btn-pl-elevated-alt
;
}
}
.profile-sidebar
{
.profile-sidebar
{
background
:
transparent
;
background
:
transparent
;
@include
float
(
right
);
@include
float
(
right
);
margin-top
:
(
$baseline
*
2
);
@include
margin-left
(
flex-gutter
()
);
width
:
flex-grid
(
3
);
width
:
flex-grid
(
3
);
box-shadow
:
0
0
1px
$shadow-l1
;
margin-top
:
(
$baseline
*
2
)
;
border
:
1px
solid
$border-color-2
;
border
-top
:
3px
solid
$blue
;
border-radius
:
3px
;
padding
:
$baseline
0
;
.user-info
{
.user-info
{
@include
clearfix
();
@include
clearfix
();
...
@@ -31,7 +48,7 @@
...
@@ -31,7 +48,7 @@
@include
box-sizing
(
border-box
);
@include
box-sizing
(
border-box
);
@include
clearfix
();
@include
clearfix
();
margin
:
0
;
margin
:
0
;
padding
:
$baseline
;
padding
:
0
;
width
:
flex-grid
(
12
);
width
:
flex-grid
(
12
);
li
{
li
{
...
@@ -59,7 +76,7 @@
...
@@ -59,7 +76,7 @@
}
}
span
.title
{
span
.title
{
@extend
%t-
copy-sub1
;
@extend
%t-
title6
;
@extend
%t-strong
;
@extend
%t-strong
;
a
{
a
{
...
...
lms/static/sass/shared/_header.scss
View file @
80cf4d6e
...
@@ -620,8 +620,8 @@ header.global-new {
...
@@ -620,8 +620,8 @@ header.global-new {
a
{
a
{
display
:block
;
display
:block
;
padding
:
3px
10px
;
padding
:
3px
10px
;
font-size
:
1
8
px
;
font-size
:
1
4
px
;
line-height
:
24px
;
line-height
:
1
.5
;
font-weight
:
600
;
font-weight
:
600
;
font-family
:
$header-sans-serif
;
font-family
:
$header-sans-serif
;
color
:
$courseware-navigation-color
;
color
:
$courseware-navigation-color
;
...
@@ -708,7 +708,6 @@ header.global-new {
...
@@ -708,7 +708,6 @@ header.global-new {
font-size
:
14px
;
font-size
:
14px
;
&
.nav-courseware-button
{
&
.nav-courseware-button
{
width
:
86px
;
text-align
:
center
;
text-align
:
center
;
margin-top
:
-3px
;
margin-top
:
-3px
;
}
}
...
@@ -826,13 +825,6 @@ header.global-new {
...
@@ -826,13 +825,6 @@ header.global-new {
.wrapper-header
{
.wrapper-header
{
padding
:
17px
0
;
padding
:
17px
0
;
}
}
.nav-global
,
.nav-courseware
{
a
{
font-size
:
18px
;
}
}
}
}
}
}
}
}
...
...
lms/templates/commerce/checkout_receipt.html
View file @
80cf4d6e
...
@@ -18,7 +18,6 @@ from django.utils.translation import ugettext as _
...
@@ -18,7 +18,6 @@ from django.utils.translation import ugettext as _
</
%
block>
</
%
block>
<
%
block
name=
"js_extra"
>
<
%
block
name=
"js_extra"
>
<
%
static:js
group=
'rwd_header'
/>
<script
src=
"${static.url('js/vendor/jquery.ajax-retry.js')}"
></script>
<script
src=
"${static.url('js/vendor/jquery.ajax-retry.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore-min.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore-min.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore.string.min.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore.string.min.js')}"
></script>
...
...
lms/templates/dashboard.html
View file @
80cf4d6e
...
@@ -147,16 +147,19 @@ from django.core.urlresolvers import reverse
...
@@ -147,16 +147,19 @@ from django.core.urlresolvers import reverse
<section
id=
"dashboard-search-results"
class=
"search-results dashboard-search-results"
></section>
<section
id=
"dashboard-search-results"
class=
"search-results dashboard-search-results"
></section>
% endif
% endif
<section
class=
"profile-sidebar"
id=
"profile-sidebar"
role=
"region"
aria-label=
"User info"
>
% if settings.FEATURES.get('IS_EDX_DOMAIN'):
<div
class=
"wrapper-find-courses"
>
<p
class=
"copy"
>
Check out our recently launched courses and what's new in your favorite subjects
</p>
<p><a
class=
"btn-find-courses"
href=
"${marketing_link('COURSES')}"
>
${_("Find New Courses")}
</a></p>
</div>
% endif
<section
class=
"profile-sidebar"
id=
"profile-sidebar"
role=
"region"
aria-label=
"Account Status Info"
>
<header
class=
"profile"
>
<header
class=
"profile"
>
<h2
class=
"
username-header"
><span
class=
"sr"
>
${_("Username")}:
</span>
</h2>
<h2
class=
"
account-status-title sr"
>
${_("Account Status Info")}:
</h2>
</header>
</header>
<section
class=
"user-info"
>
<section
class=
"user-info"
>
<ul>
<ul>
<li
class=
"heads-up"
>
<span
class=
"title"
>
${_("Want to change your account settings?")}
</span>
<span
class=
"copy"
>
${_("Click the arrow next to your username above.")}
</span>
</li>
% if len(order_history_list):
% if len(order_history_list):
<li
class=
"order-history"
>
<li
class=
"order-history"
>
...
...
lms/templates/navigation-edx.html
View file @
80cf4d6e
...
@@ -53,6 +53,7 @@ site_status_msg = get_site_status_msg(course_id)
...
@@ -53,6 +53,7 @@ site_status_msg = get_site_status_msg(course_id)
% if user.is_authenticated():
% if user.is_authenticated():
% if not course or disable_courseware_header:
% if not course or disable_courseware_header:
% if not nav_hidden:
<nav
aria-label=
"Main"
class=
"nav-main"
>
<nav
aria-label=
"Main"
class=
"nav-main"
>
<ul
class=
"left nav-global authenticated"
>
<ul
class=
"left nav-global authenticated"
>
<
%
block
name=
"navigation_global_links_authenticated"
>
<
%
block
name=
"navigation_global_links_authenticated"
>
...
@@ -68,6 +69,7 @@ site_status_msg = get_site_status_msg(course_id)
...
@@ -68,6 +69,7 @@ site_status_msg = get_site_status_msg(course_id)
</
%
block>
</
%
block>
</ul>
</ul>
</nav>
</nav>
% endif
% endif
% endif
<ul
class=
"user"
>
<ul
class=
"user"
>
...
@@ -101,44 +103,28 @@ site_status_msg = get_site_status_msg(course_id)
...
@@ -101,44 +103,28 @@ site_status_msg = get_site_status_msg(course_id)
% endif
% endif
% else:
% else:
<nav
aria-label=
"Main"
class=
"nav-main"
>
<ul
class=
"left nav-global"
>
<
%
block
name=
"navigation_global_links"
>
<li
class=
"nav-global-01"
>
<a
href=
"${marketing_link('HOW_IT_WORKS')}"
>
${_("How it Works")}
</a>
</li>
<li
class=
"nav-global-02"
>
<a
href=
"${marketing_link('COURSES')}"
>
${_("Find Courses")}
</a>
</li>
<li
class=
"nav-global-03"
>
<a
href=
"${marketing_link('SCHOOLS')}"
>
${_("Schools
&
Partners")}
</a>
</li>
</
%
block>
</ul>
</nav>
<nav
aria-label=
"Account"
class=
"nav-account-management"
>
<nav
aria-label=
"Account"
class=
"nav-account-management"
>
<div
class=
"right nav-courseware"
>
<div
class=
"right nav-courseware"
>
<div
class=
"nav-courseware-01"
>
% if not settings.FEATURES['DISABLE_LOGIN_BUTTON']:
% if course and settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain:
<a
class=
"cta cta-login"
href=
"${reverse('course-specific-login', args=[course.id.to_deprecated_string()])}${login_query()}"
>
${_("Sign in")}
</a>
% else:
<a
class=
"cta cta-login"
href=
"/login${login_query()}"
>
${_("Sign in")}
</a>
% endif
% endif
</div>
% if not settings.FEATURES['DISABLE_LOGIN_BUTTON']:
% if not settings.FEATURES['DISABLE_LOGIN_BUTTON']:
% if course and settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain:
% if course and settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain:
<div
class=
"nav-courseware-0
1
"
>
<div
class=
"nav-courseware-0
2
"
>
<a
class=
"cta cta-register"
href=
"${reverse('course-specific-register', args=[course.id.to_deprecated_string()])}"
>
${_("Register")}
</a>
<a
class=
"cta cta-register
nav-courseware-button
"
href=
"${reverse('course-specific-register', args=[course.id.to_deprecated_string()])}"
>
${_("Register")}
</a>
</div>
</div>
% else:
% else:
<div
class=
"nav-courseware-0
1
"
>
<div
class=
"nav-courseware-0
2
"
>
<a
class=
"cta cta-register"
href=
"/register"
>
${_("Register")}
</a>
<a
class=
"cta cta-register
nav-courseware-button
"
href=
"/register"
>
${_("Register")}
</a>
</div>
</div>
% endif
% endif
% endif
% endif
<div
class=
"nav-courseware-02"
>
% if not settings.FEATURES['DISABLE_LOGIN_BUTTON']:
% if course and settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain:
<a
class=
"cta cta-login nav-courseware-button"
href=
"${reverse('course-specific-login', args=[course.id.to_deprecated_string()])}${login_query()}"
>
${_("Sign in")}
</a>
% else:
<a
class=
"cta cta-login nav-courseware-button"
href=
"/login${login_query()}"
>
${_("Sign in")}
</a>
% endif
% endif
</div>
</div>
</div>
</nav>
</nav>
% endif
% endif
...
...
lms/templates/verify_student/incourse_reverify.html
View file @
80cf4d6e
...
@@ -18,7 +18,6 @@ from django.utils.translation import ugettext as _
...
@@ -18,7 +18,6 @@ from django.utils.translation import ugettext as _
% endfor
% endfor
</
%
block>
</
%
block>
<
%
block
name=
"js_extra"
>
<
%
block
name=
"js_extra"
>
<
%
static:js
group=
'rwd_header'
/>
<script
src=
"${static.url('js/vendor/underscore-min.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore-min.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore.string.min.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore.string.min.js')}"
></script>
<script
src=
"${static.url('js/vendor/backbone-min.js')}"
></script>
<script
src=
"${static.url('js/vendor/backbone-min.js')}"
></script>
...
...
lms/templates/verify_student/pay_and_verify.html
View file @
80cf4d6e
...
@@ -35,7 +35,6 @@ from verify_student.views import PayAndVerifyView
...
@@ -35,7 +35,6 @@ from verify_student.views import PayAndVerifyView
% endfor
% endfor
</
%
block>
</
%
block>
<
%
block
name=
"js_extra"
>
<
%
block
name=
"js_extra"
>
<
%
static:js
group=
'rwd_header'
/>
<script
src=
"${static.url('js/vendor/underscore-min.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore-min.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore.string.min.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore.string.min.js')}"
></script>
<script
src=
"${static.url('js/vendor/backbone-min.js')}"
></script>
<script
src=
"${static.url('js/vendor/backbone-min.js')}"
></script>
...
...
lms/templates/verify_student/reverify.html
View file @
80cf4d6e
...
@@ -16,7 +16,6 @@ from django.utils.translation import ugettext as _
...
@@ -16,7 +16,6 @@ from django.utils.translation import ugettext as _
% endfor
% endfor
</
%
block>
</
%
block>
<
%
block
name=
"js_extra"
>
<
%
block
name=
"js_extra"
>
<
%
static:js
group=
'rwd_header'
/>
<script
src=
"${static.url('js/vendor/underscore-min.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore-min.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore.string.min.js')}"
></script>
<script
src=
"${static.url('js/vendor/underscore.string.min.js')}"
></script>
<script
src=
"${static.url('js/vendor/backbone-min.js')}"
></script>
<script
src=
"${static.url('js/vendor/backbone-min.js')}"
></script>
...
...
openedx/core/djangoapps/content/course_overviews/migrations/0004_default_lowest_passing_grade_to_None.py
0 → 100644
View file @
80cf4d6e
# -*- coding: utf-8 -*-
from
south.utils
import
datetime_utils
as
datetime
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
django.db
import
models
class
Migration
(
SchemaMigration
):
def
forwards
(
self
,
orm
):
# Changing field 'CourseOverview.lowest_passing_grade'
db
.
alter_column
(
'course_overviews_courseoverview'
,
'lowest_passing_grade'
,
self
.
gf
(
'django.db.models.fields.DecimalField'
)(
null
=
True
,
max_digits
=
5
,
decimal_places
=
2
))
def
backwards
(
self
,
orm
):
# Changing field 'CourseOverview.lowest_passing_grade'
db
.
alter_column
(
'course_overviews_courseoverview'
,
'lowest_passing_grade'
,
self
.
gf
(
'django.db.models.fields.DecimalField'
)(
default
=
0.5
,
max_digits
=
5
,
decimal_places
=
2
))
models
=
{
'course_overviews.courseoverview'
:
{
'Meta'
:
{
'object_name'
:
'CourseOverview'
},
'_location'
:
(
'xmodule_django.models.UsageKeyField'
,
[],
{
'max_length'
:
'255'
}),
'_pre_requisite_courses_json'
:
(
'django.db.models.fields.TextField'
,
[],
{}),
'advertised_start'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
}),
'cert_html_view_enabled'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'cert_name_long'
:
(
'django.db.models.fields.TextField'
,
[],
{}),
'cert_name_short'
:
(
'django.db.models.fields.TextField'
,
[],
{}),
'certificates_display_behavior'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
}),
'certificates_show_before_end'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'course_image_url'
:
(
'django.db.models.fields.TextField'
,
[],
{}),
'days_early_for_beta'
:
(
'django.db.models.fields.FloatField'
,
[],
{
'null'
:
'True'
}),
'display_name'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
}),
'display_number_with_default'
:
(
'django.db.models.fields.TextField'
,
[],
{}),
'display_org_with_default'
:
(
'django.db.models.fields.TextField'
,
[],
{}),
'end'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
}),
'end_of_course_survey_url'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
}),
'facebook_url'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
}),
'has_any_active_web_certificate'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'id'
:
(
'xmodule_django.models.CourseKeyField'
,
[],
{
'max_length'
:
'255'
,
'primary_key'
:
'True'
,
'db_index'
:
'True'
}),
'lowest_passing_grade'
:
(
'django.db.models.fields.DecimalField'
,
[],
{
'null'
:
'True'
,
'max_digits'
:
'5'
,
'decimal_places'
:
'2'
}),
'mobile_available'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'social_sharing_url'
:
(
'django.db.models.fields.TextField'
,
[],
{
'null'
:
'True'
}),
'start'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
}),
'visible_to_staff_only'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
})
}
}
complete_apps
=
[
'course_overviews'
]
\ No newline at end of file
openedx/core/djangoapps/content/course_overviews/models.py
View file @
80cf4d6e
...
@@ -52,7 +52,7 @@ class CourseOverview(django.db.models.Model):
...
@@ -52,7 +52,7 @@ class CourseOverview(django.db.models.Model):
cert_name_long
=
TextField
()
cert_name_long
=
TextField
()
# Grading
# Grading
lowest_passing_grade
=
DecimalField
(
max_digits
=
5
,
decimal_places
=
2
)
lowest_passing_grade
=
DecimalField
(
max_digits
=
5
,
decimal_places
=
2
,
null
=
True
)
# Access parameters
# Access parameters
days_early_for_beta
=
FloatField
(
null
=
True
)
days_early_for_beta
=
FloatField
(
null
=
True
)
...
@@ -77,6 +77,16 @@ class CourseOverview(django.db.models.Model):
...
@@ -77,6 +77,16 @@ class CourseOverview(django.db.models.Model):
from
lms.djangoapps.certificates.api
import
get_active_web_certificate
from
lms.djangoapps.certificates.api
import
get_active_web_certificate
from
lms.djangoapps.courseware.courses
import
course_image_url
from
lms.djangoapps.courseware.courses
import
course_image_url
# Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806.
# If the course has a malformed grading policy such that
# course._grading_policy['GRADE_CUTOFFS'] = {}, then
# course.lowest_passing_grade will raise a ValueError.
# Work around this for now by defaulting to None.
try
:
lowest_passing_grade
=
course
.
lowest_passing_grade
except
ValueError
:
lowest_passing_grade
=
None
return
CourseOverview
(
return
CourseOverview
(
id
=
course
.
id
,
id
=
course
.
id
,
_location
=
course
.
location
,
_location
=
course
.
location
,
...
@@ -98,7 +108,7 @@ class CourseOverview(django.db.models.Model):
...
@@ -98,7 +108,7 @@ class CourseOverview(django.db.models.Model):
has_any_active_web_certificate
=
(
get_active_web_certificate
(
course
)
is
not
None
),
has_any_active_web_certificate
=
(
get_active_web_certificate
(
course
)
is
not
None
),
cert_name_short
=
course
.
cert_name_short
,
cert_name_short
=
course
.
cert_name_short
,
cert_name_long
=
course
.
cert_name_long
,
cert_name_long
=
course
.
cert_name_long
,
lowest_passing_grade
=
course
.
lowest_passing_grade
,
lowest_passing_grade
=
lowest_passing_grade
,
end_of_course_survey_url
=
course
.
end_of_course_survey_url
,
end_of_course_survey_url
=
course
.
end_of_course_survey_url
,
days_early_for_beta
=
course
.
days_early_for_beta
,
days_early_for_beta
=
course
.
days_early_for_beta
,
...
...
openedx/core/djangoapps/content/course_overviews/tests.py
View file @
80cf4d6e
...
@@ -294,3 +294,18 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
...
@@ -294,3 +294,18 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
# which causes get_from_id to raise an IOError.
# which causes get_from_id to raise an IOError.
with
self
.
assertRaises
(
IOError
):
with
self
.
assertRaises
(
IOError
):
CourseOverview
.
get_from_id
(
course
.
id
)
CourseOverview
.
get_from_id
(
course
.
id
)
def
test_malformed_grading_policy
(
self
):
"""
Test that CourseOverview handles courses with a malformed grading policy
such that course._grading_policy['GRADE_CUTOFFS'] = {} by defaulting
.lowest_passing_grade to None.
Created in response to https://openedx.atlassian.net/browse/TNL-2806.
"""
course
=
CourseFactory
.
create
()
course
.
_grading_policy
[
'GRADE_CUTOFFS'
]
=
{}
# pylint: disable=protected-access
with
self
.
assertRaises
(
ValueError
):
__
=
course
.
lowest_passing_grade
course_overview
=
CourseOverview
.
_create_from_course
(
course
)
# pylint: disable=protected-access
self
.
assertEqual
(
course_overview
.
lowest_passing_grade
,
None
)
openedx/core/lib/extract_tar.py
View file @
80cf4d6e
...
@@ -7,6 +7,7 @@ http://stackoverflow.com/questions/10060069/safely-extract-zip-or-tar-using-pyth
...
@@ -7,6 +7,7 @@ http://stackoverflow.com/questions/10060069/safely-extract-zip-or-tar-using-pyth
"""
"""
from
os.path
import
abspath
,
realpath
,
dirname
,
join
as
joinpath
from
os.path
import
abspath
,
realpath
,
dirname
,
join
as
joinpath
from
django.core.exceptions
import
SuspiciousOperation
from
django.core.exceptions
import
SuspiciousOperation
from
django.conf
import
settings
import
logging
import
logging
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -28,19 +29,23 @@ def _is_bad_path(path, base):
...
@@ -28,19 +29,23 @@ def _is_bad_path(path, base):
def
_is_bad_link
(
info
,
base
):
def
_is_bad_link
(
info
,
base
):
"""
"""
Does the file sym- or
d
hard-link to files outside `base`?
Does the file sym- or hard-link to files outside `base`?
"""
"""
# Links are interpreted relative to the directory containing the link
# Links are interpreted relative to the directory containing the link
tip
=
resolved
(
joinpath
(
base
,
dirname
(
info
.
name
)))
tip
=
resolved
(
joinpath
(
base
,
dirname
(
info
.
name
)))
return
_is_bad_path
(
info
.
linkname
,
base
=
tip
)
return
_is_bad_path
(
info
.
linkname
,
base
=
tip
)
def
safemembers
(
members
):
def
safemembers
(
members
,
base
):
"""
"""
Check that all elements of a tar file are safe.
Check that all elements of a tar file are safe.
"""
"""
base
=
resolved
(
"."
)
base
=
resolved
(
base
)
# check that we're not trying to import outside of the data_dir
if
not
base
.
startswith
(
resolved
(
settings
.
DATA_DIR
)):
raise
SuspiciousOperation
(
"Attempted to import course outside of data dir"
)
for
finfo
in
members
:
for
finfo
in
members
:
if
_is_bad_path
(
finfo
.
name
,
base
):
if
_is_bad_path
(
finfo
.
name
,
base
):
...
@@ -61,8 +66,8 @@ def safemembers(members):
...
@@ -61,8 +66,8 @@ def safemembers(members):
return
members
return
members
def
safetar_extractall
(
tar
f
,
*
args
,
**
kwargs
):
def
safetar_extractall
(
tar
_file
,
path
=
"."
,
members
=
None
):
# pylint: disable=unused-argument
"""
"""
Safe version of `tar
f
.extractall()`.
Safe version of `tar
_file
.extractall()`.
"""
"""
return
tar
f
.
extractall
(
members
=
safemembers
(
tarf
),
*
args
,
**
kwargs
)
return
tar
_file
.
extractall
(
path
,
safemembers
(
tar_file
,
path
)
)
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