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
import
os
from
path
import
path
from
django.core.management.base
import
BaseCommand
,
CommandError
from
django.conf
import
settings
from
tempfile
import
mkdtemp
import
tarfile
...
...
@@ -32,8 +33,8 @@ class Command(BaseCommand):
output_path
=
args
[
1
]
# Create temp directories to extract the source and create the target archive.
temp_source_dir
=
mkdtemp
()
temp_target_dir
=
mkdtemp
()
temp_source_dir
=
mkdtemp
(
dir
=
settings
.
DATA_DIR
)
temp_target_dir
=
mkdtemp
(
dir
=
settings
.
DATA_DIR
)
try
:
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.
"""
from
unittest
import
TestCase
from
django.core.management
import
call_command
,
CommandError
from
django.conf
import
settings
from
tempfile
import
mkdtemp
import
shutil
from
path
import
path
...
...
@@ -18,7 +19,7 @@ class ConvertExportFormat(TestCase):
""" Common 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
.
data_dir
=
path
(
__file__
)
.
realpath
()
.
parent
/
'data'
self
.
version0
=
self
.
data_dir
/
"Version0_drafts.tar.gz"
...
...
@@ -52,8 +53,8 @@ class ConvertExportFormat(TestCase):
"""
Helper function for determining if 2 archives are equal.
"""
temp_dir_1
=
mkdtemp
()
temp_dir_2
=
mkdtemp
()
temp_dir_1
=
mkdtemp
(
dir
=
settings
.
DATA_DIR
)
temp_dir_2
=
mkdtemp
(
dir
=
settings
.
DATA_DIR
)
try
:
extract_source
(
file1
,
temp_dir_1
)
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):
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
):
"""
Check that safety measure work.
...
...
@@ -233,6 +246,12 @@ class ImportTestCase(CourseTestCase):
try_tar
(
self
.
_symlink_tar
())
try_tar
(
self
.
_outside_tar
())
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.,
# either 3, indicating all previous steps are completed, or 0,
# indicating no upload in progress)
...
...
@@ -294,13 +313,19 @@ class ImportTestCase(CourseTestCase):
self
.
assertIn
(
test_block3
.
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
:
tar
=
tarfile
.
open
(
path
(
TEST_DATA_DIR
)
/
'imports'
/
'library.HhJfPD.tar.gz'
)
safetar_extractall
(
tar
,
extract_dir
)
with
tarfile
.
open
(
path
(
TEST_DATA_DIR
)
/
'imports'
/
'library.HhJfPD.tar.gz'
)
as
tar
:
safetar_extractall
(
tar
,
extract_dir
)
library_items
=
import_library_from_xml
(
self
.
store
,
self
.
user
.
id
,
settings
.
GITHUB_REPO_ROOT
,
[
extract_dir
/
'library'
],
self
.
store
,
self
.
user
.
id
,
settings
.
GITHUB_REPO_ROOT
,
[
extract_dir_relative
/
'library'
],
load_error_modules
=
False
,
static_content_store
=
contentstore
(),
target_id
=
lib_key
...
...
cms/envs/bok_choy.py
View file @
80cf4d6e
...
...
@@ -39,6 +39,7 @@ INSTALLED_APPS += ('django_extensions',)
TEST_ROOT
=
REPO_ROOT
/
"test_root"
# pylint: disable=no-value-for-parameter
GITHUB_REPO_ROOT
=
(
TEST_ROOT
/
"data"
)
.
abspath
()
LOG_DIR
=
(
TEST_ROOT
/
"log"
)
.
abspath
()
DATA_DIR
=
TEST_ROOT
/
"data"
# Configure modulestore to use the test folder within the repo
update_module_store_settings
(
...
...
cms/envs/test.py
View file @
80cf4d6e
...
...
@@ -65,6 +65,7 @@ TEST_ROOT = path('test_root')
STATIC_ROOT
=
TEST_ROOT
/
"staticfiles"
GITHUB_REPO_ROOT
=
TEST_ROOT
/
"data"
DATA_DIR
=
TEST_ROOT
/
"data"
COMMON_TEST_DATA_ROOT
=
COMMON_ROOT
/
"test"
/
"data"
# For testing "push to lms"
...
...
common/djangoapps/course_modes/tests/test_views.py
View file @
80cf4d6e
...
...
@@ -325,6 +325,22 @@ class CourseModeViewTest(UrlResetMixin, ModuleStoreTestCase):
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'
)
class
TrackSelectionEmbargoTest
(
UrlResetMixin
,
ModuleStoreTestCase
):
...
...
common/djangoapps/course_modes/views.py
View file @
80cf4d6e
...
...
@@ -119,7 +119,8 @@ class ChooseModeView(View):
"course_num"
:
course
.
display_number_with_default
,
"chosen_price"
:
chosen_price
,
"error"
:
error
,
"responsive"
:
True
"responsive"
:
True
,
"nav_hidden"
:
True
,
}
if
"verified"
in
modes
:
context
[
"suggested_prices"
]
=
[
...
...
common/djangoapps/student/tests/tests.py
View file @
80cf4d6e
...
...
@@ -529,6 +529,19 @@ class DashboardTest(ModuleStoreTestCase):
response_3
=
self
.
client
.
get
(
reverse
(
'dashboard'
))
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
):
"""
...
...
common/djangoapps/student/views.py
View file @
80cf4d6e
...
...
@@ -694,6 +694,7 @@ def dashboard(request):
'order_history_list'
:
order_history_list
,
'courses_requirements_not_met'
:
courses_requirements_not_met
,
'ccx_membership_triplets'
:
ccx_membership_triplets
,
'nav_hidden'
:
True
,
}
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):
if
field
.
scope
.
user
==
UserScope
.
ONE
:
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)
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):
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
):
"""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):
course_names
=
self
.
dashboard_page
.
wait_for_page
()
.
available_courses
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
):
# Navigate to the registration page
self
.
register_page
.
visit
()
...
...
lms/djangoapps/commerce/tests/test_views.py
View file @
80cf4d6e
...
...
@@ -4,6 +4,7 @@ from uuid import uuid4
from
nose.plugins.attrib
import
attr
import
ddt
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.test
import
TestCase
import
mock
...
...
@@ -84,3 +85,14 @@ class ReceiptViewTests(UserMixin, TestCase):
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
.
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):
'error_text'
:
error_text
,
'for_help_text'
:
for_help_text
,
'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
)
lms/djangoapps/courseware/tests/test_module_render.py
View file @
80cf4d6e
...
...
@@ -1360,23 +1360,44 @@ class TestRebindModule(TestSubmittingProblems):
super
(
TestRebindModule
,
self
)
.
setUp
()
self
.
homework
=
self
.
add_graded_section_to_course
(
'homework'
)
self
.
lti
=
ItemFactory
.
create
(
category
=
'lti'
,
parent
=
self
.
homework
)
self
.
problem
=
ItemFactory
.
create
(
category
=
'problem'
,
parent
=
self
.
homework
)
self
.
user
=
UserFactory
.
create
()
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"""
mock_request
=
MagicMock
()
mock_request
.
user
=
user
field_data_cache
=
FieldDataCache
.
cache_for_descriptor_descendents
(
self
.
course
.
id
,
user
,
self
.
course
,
depth
=
2
)
if
item
is
None
:
item
=
self
.
lti
return
render
.
get_module
(
# pylint: disable=protected-access
user
,
mock_request
,
self
.
lti
.
location
,
item
.
location
,
field_data_cache
,
)
.
_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
):
"""
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):
)
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
):
# We've already paid, and now we're trying to verify
course
=
self
.
_create_course
(
"verified"
)
...
...
lms/djangoapps/verify_student/views.py
View file @
80cf4d6e
...
...
@@ -376,6 +376,7 @@ class PayAndVerifyView(View):
'already_verified'
:
already_verified
,
'verification_good_until'
:
verification_good_until
,
'capture_sound'
:
staticfiles_storage
.
url
(
"audio/camera_capture.wav"
),
'nav_hidden'
:
True
,
}
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'):
##################### Credit Provider help link ####################
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 = (
)
dashboard_search_js
=
[
'js/search/dashboard/main.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'
))
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'
))
...
...
@@ -1260,7 +1259,6 @@ instructor_dash_js = (
# These are not courseware, so they do not need many of the courseware-specific
# JavaScript modules.
student_account_js
=
[
'js/utils/rwd_header.js'
,
'js/utils/edx.utils.validate.js'
,
'js/form.ext.js'
,
'js/my_courses_dropdown.js'
,
...
...
@@ -1549,10 +1547,6 @@ PIPELINE_JS = {
'source_filenames'
:
dashboard_search_js
,
'output_filename'
:
'js/dashboard-search.js'
,
},
'rwd_header'
:
{
'source_filenames'
:
rwd_header_js
,
'output_filename'
:
'js/rwd_header.js'
},
'student_account'
:
{
'source_filenames'
:
student_account_js
,
'output_filename'
:
'js/student_account.js'
...
...
lms/static/sass/elements/_controls.scss
View file @
80cf4d6e
...
...
@@ -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
...
...
lms/static/sass/multicourse/_dashboard.scss
View file @
80cf4d6e
...
...
@@ -15,14 +15,31 @@
@include
clearfix
();
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
{
background
:
transparent
;
@include
float
(
right
);
margin-top
:
(
$baseline
*
2
);
@include
margin-left
(
flex-gutter
()
);
width
:
flex-grid
(
3
);
box-shadow
:
0
0
1px
$shadow-l1
;
border
:
1px
solid
$border-color-2
;
border-radius
:
3px
;
margin-top
:
(
$baseline
*
2
)
;
border
-top
:
3px
solid
$blue
;
padding
:
$baseline
0
;
.user-info
{
@include
clearfix
();
...
...
@@ -31,7 +48,7 @@
@include
box-sizing
(
border-box
);
@include
clearfix
();
margin
:
0
;
padding
:
$baseline
;
padding
:
0
;
width
:
flex-grid
(
12
);
li
{
...
...
@@ -59,7 +76,7 @@
}
span
.title
{
@extend
%t-
copy-sub1
;
@extend
%t-
title6
;
@extend
%t-strong
;
a
{
...
...
lms/static/sass/shared/_header.scss
View file @
80cf4d6e
...
...
@@ -620,8 +620,8 @@ header.global-new {
a
{
display
:block
;
padding
:
3px
10px
;
font-size
:
1
8
px
;
line-height
:
24px
;
font-size
:
1
4
px
;
line-height
:
1
.5
;
font-weight
:
600
;
font-family
:
$header-sans-serif
;
color
:
$courseware-navigation-color
;
...
...
@@ -708,7 +708,6 @@ header.global-new {
font-size
:
14px
;
&
.nav-courseware-button
{
width
:
86px
;
text-align
:
center
;
margin-top
:
-3px
;
}
...
...
@@ -826,13 +825,6 @@ header.global-new {
.wrapper-header
{
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 _
</
%
block>
<
%
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/underscore-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
<section
id=
"dashboard-search-results"
class=
"search-results dashboard-search-results"
></section>
% 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"
>
<h2
class=
"
username-header"
><span
class=
"sr"
>
${_("Username")}:
</span>
</h2>
<h2
class=
"
account-status-title sr"
>
${_("Account Status Info")}:
</h2>
</header>
<section
class=
"user-info"
>
<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):
<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)
% if user.is_authenticated():
% if not course or disable_courseware_header:
% if not nav_hidden:
<nav
aria-label=
"Main"
class=
"nav-main"
>
<ul
class=
"left nav-global authenticated"
>
<
%
block
name=
"navigation_global_links_authenticated"
>
...
...
@@ -68,6 +69,7 @@ site_status_msg = get_site_status_msg(course_id)
</
%
block>
</ul>
</nav>
% endif
% endif
<ul
class=
"user"
>
...
...
@@ -101,44 +103,28 @@ site_status_msg = get_site_status_msg(course_id)
% endif
% 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"
>
<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 course and settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain:
<div
class=
"nav-courseware-0
1
"
>
<a
class=
"cta cta-register"
href=
"${reverse('course-specific-register', args=[course.id.to_deprecated_string()])}"
>
${_("Register")}
</a>
<div
class=
"nav-courseware-0
2
"
>
<a
class=
"cta cta-register
nav-courseware-button
"
href=
"${reverse('course-specific-register', args=[course.id.to_deprecated_string()])}"
>
${_("Register")}
</a>
</div>
% else:
<div
class=
"nav-courseware-0
1
"
>
<a
class=
"cta cta-register"
href=
"/register"
>
${_("Register")}
</a>
<div
class=
"nav-courseware-0
2
"
>
<a
class=
"cta cta-register
nav-courseware-button
"
href=
"/register"
>
${_("Register")}
</a>
</div>
% 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>
</nav>
% endif
...
...
lms/templates/verify_student/incourse_reverify.html
View file @
80cf4d6e
...
...
@@ -18,7 +18,6 @@ from django.utils.translation import ugettext as _
% endfor
</
%
block>
<
%
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.string.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
% endfor
</
%
block>
<
%
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.string.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 _
% endfor
</
%
block>
<
%
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.string.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):
cert_name_long
=
TextField
()
# 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
days_early_for_beta
=
FloatField
(
null
=
True
)
...
...
@@ -77,6 +77,16 @@ class CourseOverview(django.db.models.Model):
from
lms.djangoapps.certificates.api
import
get_active_web_certificate
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
(
id
=
course
.
id
,
_location
=
course
.
location
,
...
...
@@ -98,7 +108,7 @@ class CourseOverview(django.db.models.Model):
has_any_active_web_certificate
=
(
get_active_web_certificate
(
course
)
is
not
None
),
cert_name_short
=
course
.
cert_name_short
,
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
,
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):
# which causes get_from_id to raise an IOError.
with
self
.
assertRaises
(
IOError
):
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
"""
from
os.path
import
abspath
,
realpath
,
dirname
,
join
as
joinpath
from
django.core.exceptions
import
SuspiciousOperation
from
django.conf
import
settings
import
logging
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -28,19 +29,23 @@ def _is_bad_path(path, 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
tip
=
resolved
(
joinpath
(
base
,
dirname
(
info
.
name
)))
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.
"""
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
:
if
_is_bad_path
(
finfo
.
name
,
base
):
...
...
@@ -61,8 +66,8 @@ def safemembers(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