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
2b8441a0
Commit
2b8441a0
authored
Dec 08, 2015
by
Nimisha Asthagiri
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update Course Catalog to use CourseOverview
parent
801165b2
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
170 additions
and
221 deletions
+170
-221
common/djangoapps/enrollment/tests/test_views.py
+5
-6
lms/djangoapps/branding/__init__.py
+3
-8
lms/djangoapps/class_dashboard/views.py
+2
-2
lms/djangoapps/course_wiki/middleware.py
+2
-2
lms/djangoapps/courseware/access.py
+27
-67
lms/djangoapps/courseware/courses.py
+50
-49
lms/djangoapps/courseware/tests/helpers.py
+7
-11
lms/djangoapps/courseware/tests/test_access.py
+23
-34
lms/djangoapps/courseware/tests/test_courses.py
+17
-4
lms/djangoapps/courseware/views.py
+12
-9
lms/djangoapps/django_comment_client/base/views.py
+2
-2
lms/templates/course.html
+7
-9
lms/templates/courseware/course_about.html
+8
-8
lms/templates/dashboard/_dashboard_course_listing.html
+1
-2
lms/templates/shoppingcart/receipt.html
+1
-2
lms/templates/shoppingcart/registration_code_receipt.html
+1
-2
lms/templates/shoppingcart/registration_code_redemption.html
+1
-2
lms/templates/shoppingcart/shopping_cart.html
+1
-2
No files found.
common/djangoapps/enrollment/tests/test_views.py
View file @
2b8441a0
...
@@ -150,10 +150,9 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
...
@@ -150,10 +150,9 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
throttle
=
EnrollmentUserThrottle
()
throttle
=
EnrollmentUserThrottle
()
self
.
rate_limit
,
rate_duration
=
throttle
.
parse_rate
(
throttle
.
rate
)
self
.
rate_limit
,
rate_duration
=
throttle
.
parse_rate
(
throttle
.
rate
)
self
.
course
=
CourseFactory
.
create
()
# Pass emit_signals when creating the course so it would be cached
# Load a CourseOverview. This initial load should result in a cache
# as a CourseOverview.
# miss; the modulestore is queried and course metadata is cached.
self
.
course
=
CourseFactory
.
create
(
emit_signals
=
True
)
__
=
CourseOverview
.
get_from_id
(
self
.
course
.
id
)
self
.
user
=
UserFactory
.
create
(
self
.
user
=
UserFactory
.
create
(
username
=
self
.
USERNAME
,
username
=
self
.
USERNAME
,
...
@@ -336,7 +335,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
...
@@ -336,7 +335,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
requesting user.
requesting user.
"""
"""
# Create another course, and enroll self.user in both courses.
# Create another course, and enroll self.user in both courses.
other_course
=
CourseFactory
.
create
()
other_course
=
CourseFactory
.
create
(
emit_signals
=
True
)
for
course
in
self
.
course
,
other_course
:
for
course
in
self
.
course
,
other_course
:
CourseModeFactory
.
create
(
CourseModeFactory
.
create
(
course_id
=
unicode
(
course
.
id
),
course_id
=
unicode
(
course
.
id
),
...
@@ -345,7 +344,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
...
@@ -345,7 +344,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
)
)
self
.
assert_enrollment_status
(
self
.
assert_enrollment_status
(
course_id
=
unicode
(
course
.
id
),
course_id
=
unicode
(
course
.
id
),
max_mongo_calls
=
1
,
max_mongo_calls
=
0
,
)
)
# Verify the user himself can see both of his enrollments.
# Verify the user himself can see both of his enrollments.
self
.
_assert_enrollments_visible_in_list
([
self
.
course
,
other_course
])
self
.
_assert_enrollments_visible_in_list
([
self
.
course
,
other_course
])
...
...
lms/djangoapps/branding/__init__.py
View file @
2b8441a0
...
@@ -14,27 +14,22 @@ from django.conf import settings
...
@@ -14,27 +14,22 @@ from django.conf import settings
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
microsite_configuration
import
microsite
from
microsite_configuration
import
microsite
from
django.contrib.staticfiles.storage
import
staticfiles_storage
from
django.contrib.staticfiles.storage
import
staticfiles_storage
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
def
get_visible_courses
():
def
get_visible_courses
():
"""
"""
Return the set of CourseDescriptors that should be visible in this branded instance
Return the set of CourseDescriptors that should be visible in this branded instance
"""
"""
filtered_by_org
=
microsite
.
get_value
(
'course_org_filter'
)
filtered_by_org
=
microsite
.
get_value
(
'course_org_filter'
)
courses
=
CourseOverview
.
get_all_courses
(
org
=
filtered_by_org
)
_courses
=
modulestore
()
.
get_courses
(
org
=
filtered_by_org
)
courses
=
[
c
for
c
in
_courses
if
isinstance
(
c
,
CourseDescriptor
)]
courses
=
sorted
(
courses
,
key
=
lambda
course
:
course
.
number
)
courses
=
sorted
(
courses
,
key
=
lambda
course
:
course
.
number
)
subdomain
=
microsite
.
get_value
(
'subdomain'
,
'default'
)
# See if we have filtered course listings in this domain
# See if we have filtered course listings in this domain
filtered_visible_ids
=
None
filtered_visible_ids
=
None
# this is legacy format which is outside of the microsite feature -- also handle dev case, which should not filter
# this is legacy format which is outside of the microsite feature -- also handle dev case, which should not filter
subdomain
=
microsite
.
get_value
(
'subdomain'
,
'default'
)
if
hasattr
(
settings
,
'COURSE_LISTINGS'
)
and
subdomain
in
settings
.
COURSE_LISTINGS
and
not
settings
.
DEBUG
:
if
hasattr
(
settings
,
'COURSE_LISTINGS'
)
and
subdomain
in
settings
.
COURSE_LISTINGS
and
not
settings
.
DEBUG
:
filtered_visible_ids
=
frozenset
(
filtered_visible_ids
=
frozenset
(
[
SlashSeparatedCourseKey
.
from_deprecated_string
(
c
)
for
c
in
settings
.
COURSE_LISTINGS
[
subdomain
]]
[
SlashSeparatedCourseKey
.
from_deprecated_string
(
c
)
for
c
in
settings
.
COURSE_LISTINGS
[
subdomain
]]
...
...
lms/djangoapps/class_dashboard/views.py
View file @
2b8441a0
...
@@ -8,7 +8,7 @@ import json
...
@@ -8,7 +8,7 @@ import json
from
django.http
import
HttpResponse
from
django.http
import
HttpResponse
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
courseware.courses
import
get_course_with_access
from
courseware.courses
import
get_course_
overview_
with_access
from
courseware.access
import
has_access
from
courseware.access
import
has_access
from
class_dashboard
import
dashboard_data
from
class_dashboard
import
dashboard_data
...
@@ -21,7 +21,7 @@ def has_instructor_access_for_class(user, course_id):
...
@@ -21,7 +21,7 @@ def has_instructor_access_for_class(user, course_id):
Returns true if the `user` is an instructor for the course.
Returns true if the `user` is an instructor for the course.
"""
"""
course
=
get_course_
with_access
(
user
,
'staff'
,
course_id
,
depth
=
None
)
course
=
get_course_
overview_with_access
(
user
,
'staff'
,
course_id
)
return
bool
(
has_access
(
user
,
'staff'
,
course
))
return
bool
(
has_access
(
user
,
'staff'
,
course
))
...
...
lms/djangoapps/course_wiki/middleware.py
View file @
2b8441a0
...
@@ -6,7 +6,7 @@ from django.shortcuts import redirect
...
@@ -6,7 +6,7 @@ from django.shortcuts import redirect
from
django.core.exceptions
import
PermissionDenied
from
django.core.exceptions
import
PermissionDenied
from
wiki.models
import
reverse
from
wiki.models
import
reverse
from
courseware.courses
import
get_course_with_access
from
courseware.courses
import
get_course_with_access
,
get_course_overview_with_access
from
courseware.access
import
has_access
from
courseware.access
import
has_access
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
from
util.request
import
course_id_from_url
from
util.request
import
course_id_from_url
...
@@ -29,7 +29,7 @@ class WikiAccessMiddleware(object):
...
@@ -29,7 +29,7 @@ class WikiAccessMiddleware(object):
if
course_id
:
if
course_id
:
# See if we are able to view the course. If we are, redirect to it
# See if we are able to view the course. If we are, redirect to it
try
:
try
:
_course
=
get_course
_with_access
(
request
.
user
,
'load'
,
course_id
)
get_course_overview
_with_access
(
request
.
user
,
'load'
,
course_id
)
return
redirect
(
"/courses/{course_id}/wiki/{path}"
.
format
(
course_id
=
course_id
.
to_deprecated_string
(),
path
=
wiki_path
))
return
redirect
(
"/courses/{course_id}/wiki/{path}"
.
format
(
course_id
=
course_id
.
to_deprecated_string
(),
path
=
wiki_path
))
except
Http404
:
except
Http404
:
# Even though we came from the course, we can't see it. So don't worry about it.
# Even though we came from the course, we can't see it. So don't worry about it.
...
...
lms/djangoapps/courseware/access.py
View file @
2b8441a0
...
@@ -105,10 +105,10 @@ def has_access(user, action, obj, course_key=None):
...
@@ -105,10 +105,10 @@ def has_access(user, action, obj, course_key=None):
# delegate the work to type-specific functions.
# delegate the work to type-specific functions.
# (start with more specific types, then get more general)
# (start with more specific types, then get more general)
if
isinstance
(
obj
,
CourseDescriptor
):
if
isinstance
(
obj
,
CourseDescriptor
):
return
_has_access_course
_desc
(
user
,
action
,
obj
)
return
_has_access_course
(
user
,
action
,
obj
)
if
isinstance
(
obj
,
CourseOverview
):
if
isinstance
(
obj
,
CourseOverview
):
return
_has_access_course
_overview
(
user
,
action
,
obj
)
return
_has_access_course
(
user
,
action
,
obj
)
if
isinstance
(
obj
,
ErrorDescriptor
):
if
isinstance
(
obj
,
ErrorDescriptor
):
return
_has_access_error_desc
(
user
,
action
,
obj
,
course_key
)
return
_has_access_error_desc
(
user
,
action
,
obj
,
course_key
)
...
@@ -202,7 +202,7 @@ def _can_load_course_on_mobile(user, course):
...
@@ -202,7 +202,7 @@ def _can_load_course_on_mobile(user, course):
be checked by callers in *addition* to the return value of this function.
be checked by callers in *addition* to the return value of this function.
Arguments:
Arguments:
user (User): the user whose course access
we are checking.
user (User): the user whose course access we are checking.
course (CourseDescriptor|CourseOverview): the course for which we are
course (CourseDescriptor|CourseOverview): the course for which we are
checking access.
checking access.
...
@@ -270,7 +270,7 @@ def _can_enroll_courselike(user, courselike):
...
@@ -270,7 +270,7 @@ def _can_enroll_courselike(user, courselike):
return
ACCESS_DENIED
return
ACCESS_DENIED
def
_has_access_course
like
(
user
,
action
,
courselike
):
def
_has_access_course
(
user
,
action
,
courselike
):
"""
"""
Check if user has access to a course.
Check if user has access to a course.
...
@@ -297,11 +297,21 @@ def _has_access_courselike(user, action, courselike):
...
@@ -297,11 +297,21 @@ def _has_access_courselike(user, action, courselike):
NOTE: this is not checking whether user is actually enrolled in the course.
NOTE: this is not checking whether user is actually enrolled in the course.
"""
"""
# delegate to generic descriptor check to check start dates
response
=
(
return
_has_access_descriptor
(
user
,
'load'
,
course
,
course
.
id
)
_visible_to_nonstaff_users
(
courselike
)
and
_can_access_descriptor_with_start_date
(
user
,
courselike
,
courselike
.
id
)
)
return
(
ACCESS_GRANTED
if
(
response
or
_has_staff_access_to_descriptor
(
user
,
courselike
,
courselike
.
id
))
else
response
)
def
can_enroll
():
def
can_enroll
():
return
_can_enroll_courselike
(
user
,
course
)
"""
Returns whether the user can enroll in the course.
"""
return
_can_enroll_courselike
(
user
,
courselike
)
def
see_exists
():
def
see_exists
():
"""
"""
...
@@ -317,8 +327,8 @@ def _has_access_courselike(user, action, courselike):
...
@@ -317,8 +327,8 @@ def _has_access_courselike(user, action, courselike):
but also allow course staff to see this.
but also allow course staff to see this.
"""
"""
return
(
return
(
_has_catalog_visibility
(
course
,
CATALOG_VISIBILITY_CATALOG_AND_ABOUT
)
_has_catalog_visibility
(
course
like
,
CATALOG_VISIBILITY_CATALOG_AND_ABOUT
)
or
_has_staff_access_to_descriptor
(
user
,
course
,
cours
e
.
id
)
or
_has_staff_access_to_descriptor
(
user
,
course
like
,
courselik
e
.
id
)
)
)
def
can_see_about_page
():
def
can_see_about_page
():
...
@@ -328,75 +338,25 @@ def _has_access_courselike(user, action, courselike):
...
@@ -328,75 +338,25 @@ def _has_access_courselike(user, action, courselike):
but also allow course staff to see this.
but also allow course staff to see this.
"""
"""
return
(
return
(
_has_catalog_visibility
(
course
,
CATALOG_VISIBILITY_CATALOG_AND_ABOUT
)
_has_catalog_visibility
(
course
like
,
CATALOG_VISIBILITY_CATALOG_AND_ABOUT
)
or
_has_catalog_visibility
(
course
,
CATALOG_VISIBILITY_ABOUT
)
or
_has_catalog_visibility
(
course
like
,
CATALOG_VISIBILITY_ABOUT
)
or
_has_staff_access_to_descriptor
(
user
,
course
,
cours
e
.
id
)
or
_has_staff_access_to_descriptor
(
user
,
course
like
,
courselik
e
.
id
)
)
)
checkers
=
{
checkers
=
{
'load'
:
can_load
,
'load'
:
can_load
,
'view_courseware_with_prerequisites'
:
'view_courseware_with_prerequisites'
:
lambda
:
_can_view_courseware_with_prerequisites
(
user
,
course
),
lambda
:
_can_view_courseware_with_prerequisites
(
user
,
course
like
),
'load_mobile'
:
lambda
:
can_load
()
and
_can_load_course_on_mobile
(
user
,
course
),
'load_mobile'
:
lambda
:
can_load
()
and
_can_load_course_on_mobile
(
user
,
course
like
),
'enroll'
:
can_enroll
,
'enroll'
:
can_enroll
,
'see_exists'
:
see_exists
,
'see_exists'
:
see_exists
,
'staff'
:
lambda
:
_has_staff_access_to_descriptor
(
user
,
course
,
cours
e
.
id
),
'staff'
:
lambda
:
_has_staff_access_to_descriptor
(
user
,
course
like
,
courselik
e
.
id
),
'instructor'
:
lambda
:
_has_instructor_access_to_descriptor
(
user
,
course
,
cours
e
.
id
),
'instructor'
:
lambda
:
_has_instructor_access_to_descriptor
(
user
,
course
like
,
courselik
e
.
id
),
'see_in_catalog'
:
can_see_in_catalog
,
'see_in_catalog'
:
can_see_in_catalog
,
'see_about_page'
:
can_see_about_page
,
'see_about_page'
:
can_see_about_page
,
}
}
return
_dispatch
(
checkers
,
action
,
user
,
course
)
return
_dispatch
(
checkers
,
action
,
user
,
courselike
)
def
_can_load_course_overview
(
user
,
course_overview
):
"""
Check if a user can load a course overview.
Arguments:
user (User): the user whose course access we are checking.
course_overview (CourseOverview): a course overview.
Note:
The user doesn't have to be enrolled in the course in order to have load
load access.
"""
response
=
(
_visible_to_nonstaff_users
(
course_overview
)
and
_can_access_descriptor_with_start_date
(
user
,
course_overview
,
course_overview
.
id
)
)
return
(
ACCESS_GRANTED
if
(
response
or
_has_staff_access_to_descriptor
(
user
,
course_overview
,
course_overview
.
id
))
else
response
)
_COURSE_OVERVIEW_CHECKERS
=
{
'enroll'
:
_can_enroll_courselike
,
'load'
:
_can_load_course_overview
,
'load_mobile'
:
lambda
user
,
course_overview
:
(
_can_load_course_overview
(
user
,
course_overview
)
and
_can_load_course_on_mobile
(
user
,
course_overview
)
),
'view_courseware_with_prerequisites'
:
_can_view_courseware_with_prerequisites
}
COURSE_OVERVIEW_SUPPORTED_ACTIONS
=
_COURSE_OVERVIEW_CHECKERS
.
keys
()
def
_has_access_course_overview
(
user
,
action
,
course_overview
):
"""
Check if user has access to a course overview.
Arguments:
user (User): the user whose course access we are checking.
action (str): the action the user is trying to perform.
See COURSE_OVERVIEW_SUPPORTED_ACTIONS for valid values.
course_overview (CourseOverview): overview of the course in question.
"""
if
action
in
_COURSE_OVERVIEW_CHECKERS
:
return
_COURSE_OVERVIEW_CHECKERS
[
action
](
user
,
course_overview
)
else
:
raise
ValueError
(
u"Unknown action for object type 'CourseOverview': '{}'"
.
format
(
action
))
def
_has_access_error_desc
(
user
,
action
,
descriptor
,
course_key
):
def
_has_access_error_desc
(
user
,
action
,
descriptor
,
course_key
):
...
...
lms/djangoapps/courseware/courses.py
View file @
2b8441a0
...
@@ -14,7 +14,6 @@ from django.conf import settings
...
@@ -14,7 +14,6 @@ from django.conf import settings
from
edxmako.shortcuts
import
render_to_string
from
edxmako.shortcuts
import
render_to_string
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore
import
ModuleStoreEnum
from
opaque_keys.edx.keys
import
CourseKey
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
static_replace
import
replace_static_urls
from
static_replace
import
replace_static_urls
...
@@ -37,6 +36,7 @@ from student.models import CourseEnrollment
...
@@ -37,6 +36,7 @@ from student.models import CourseEnrollment
import
branding
import
branding
from
opaque_keys.edx.keys
import
UsageKey
from
opaque_keys.edx.keys
import
UsageKey
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -58,7 +58,6 @@ def get_course(course_id, depth=0):
...
@@ -58,7 +58,6 @@ def get_course(course_id, depth=0):
return
course
return
course
# TODO please rename this function to get_course_by_key at next opportunity!
def
get_course_by_id
(
course_key
,
depth
=
0
):
def
get_course_by_id
(
course_key
,
depth
=
0
):
"""
"""
Given a course id, return the corresponding course descriptor.
Given a course id, return the corresponding course descriptor.
...
@@ -94,9 +93,39 @@ def get_course_with_access(user, action, course_key, depth=0, check_if_enrolled=
...
@@ -94,9 +93,39 @@ def get_course_with_access(user, action, course_key, depth=0, check_if_enrolled=
check_if_enrolled: If true, additionally verifies that the user is either enrolled in the course
check_if_enrolled: If true, additionally verifies that the user is either enrolled in the course
or has staff access.
or has staff access.
"""
"""
assert
isinstance
(
course_key
,
CourseKey
)
course
=
get_course_by_id
(
course_key
,
depth
)
course
=
get_course_by_id
(
course_key
,
depth
=
depth
)
check_course_access
(
course
,
user
,
action
,
check_if_enrolled
)
access_response
=
has_access
(
user
,
action
,
course
,
course_key
)
return
course
def
get_course_overview_with_access
(
user
,
action
,
course_key
,
check_if_enrolled
=
False
):
"""
Given a course_key, look up the corresponding course overview,
check that the user has the access to perform the specified action
on the course, and return the course overview.
Raises a 404 if the course_key is invalid, or the user doesn't have access.
check_if_enrolled: If true, additionally verifies that the user is either enrolled in the course
or has staff access.
"""
try
:
course_overview
=
CourseOverview
.
get_from_id
(
course_key
)
except
CourseOverview
.
DoesNotExist
:
raise
Http404
(
"Course not found."
)
check_course_access
(
course_overview
,
user
,
action
,
check_if_enrolled
)
return
course_overview
def
check_course_access
(
course
,
user
,
action
,
check_if_enrolled
=
False
):
"""
Check that the user has the access to perform the specified action
on the course (CourseDescriptor|CourseOverview).
check_if_enrolled: If true, additionally verifies that the user is either
enrolled in the course or has staff access.
"""
access_response
=
has_access
(
user
,
action
,
course
,
course
.
id
)
if
not
access_response
:
if
not
access_response
:
# Deliberately return a non-specific error message to avoid
# Deliberately return a non-specific error message to avoid
...
@@ -104,12 +133,11 @@ def get_course_with_access(user, action, course_key, depth=0, check_if_enrolled=
...
@@ -104,12 +133,11 @@ def get_course_with_access(user, action, course_key, depth=0, check_if_enrolled=
raise
CoursewareAccessException
(
access_response
)
raise
CoursewareAccessException
(
access_response
)
if
check_if_enrolled
:
if
check_if_enrolled
:
# Verify that the user is either enrolled in the course or a staff member.
# Verify that the user is either enrolled in the course or a staff
# If user is not enrolled, raise UserNotEnrolled exception that will be caught by middleware.
# member. If user is not enrolled, raise UserNotEnrolled exception
if
not
((
user
.
id
and
CourseEnrollment
.
is_enrolled
(
user
,
course_key
))
or
has_access
(
user
,
'staff'
,
course
)):
# that will be caught by middleware.
raise
UserNotEnrolled
(
course_key
)
if
not
((
user
.
id
and
CourseEnrollment
.
is_enrolled
(
user
,
course
.
id
))
or
has_access
(
user
,
'staff'
,
course
)):
raise
UserNotEnrolled
(
course
.
id
)
return
course
def
find_file
(
filesystem
,
dirs
,
filename
):
def
find_file
(
filesystem
,
dirs
,
filename
):
...
@@ -129,16 +157,6 @@ def find_file(filesystem, dirs, filename):
...
@@ -129,16 +157,6 @@ def find_file(filesystem, dirs, filename):
raise
ResourceNotFoundError
(
u"Could not find {0}"
.
format
(
filename
))
raise
ResourceNotFoundError
(
u"Could not find {0}"
.
format
(
filename
))
def
get_course_university_about_section
(
course
):
# pylint: disable=invalid-name
"""
Returns a snippet of HTML displaying the course's university.
Arguments:
course (CourseDescriptor|CourseOverview): A course.
"""
return
course
.
display_org_with_default
def
get_course_about_section
(
request
,
course
,
section_key
):
def
get_course_about_section
(
request
,
course
,
section_key
):
"""
"""
This returns the snippet of html to be rendered on the course about page,
This returns the snippet of html to be rendered on the course about page,
...
@@ -146,9 +164,6 @@ def get_course_about_section(request, course, section_key):
...
@@ -146,9 +164,6 @@ def get_course_about_section(request, course, section_key):
Valid keys:
Valid keys:
- overview
- overview
- title
- university
- number
- short_description
- short_description
- description
- description
- key_dates (includes start, end, exams, etc)
- key_dates (includes start, end, exams, etc)
...
@@ -159,6 +174,7 @@ def get_course_about_section(request, course, section_key):
...
@@ -159,6 +174,7 @@ def get_course_about_section(request, course, section_key):
- syllabus
- syllabus
- textbook
- textbook
- faq
- faq
- effort
- more_info
- more_info
- ocw_links
- ocw_links
"""
"""
...
@@ -167,7 +183,6 @@ def get_course_about_section(request, course, section_key):
...
@@ -167,7 +183,6 @@ def get_course_about_section(request, course, section_key):
# markup. This can change without effecting this interface when we find a
# markup. This can change without effecting this interface when we find a
# good format for defining so many snippets of text/html.
# good format for defining so many snippets of text/html.
# TODO: Remove number, instructors from this set
html_sections
=
{
html_sections
=
{
'short_description'
,
'short_description'
,
'description'
,
'description'
,
...
@@ -180,8 +195,6 @@ def get_course_about_section(request, course, section_key):
...
@@ -180,8 +195,6 @@ def get_course_about_section(request, course, section_key):
'textbook'
,
'textbook'
,
'faq'
,
'faq'
,
'more_info'
,
'more_info'
,
'number'
,
'instructors'
,
'overview'
,
'overview'
,
'effort'
,
'effort'
,
'end_date'
,
'end_date'
,
...
@@ -225,12 +238,6 @@ def get_course_about_section(request, course, section_key):
...
@@ -225,12 +238,6 @@ def get_course_about_section(request, course, section_key):
section_key
,
course
.
location
.
to_deprecated_string
()
section_key
,
course
.
location
.
to_deprecated_string
()
)
)
return
None
return
None
elif
section_key
==
"title"
:
return
course
.
display_name_with_default
elif
section_key
==
"university"
:
return
get_course_university_about_section
(
course
)
elif
section_key
==
"number"
:
return
course
.
display_number_with_default
raise
KeyError
(
"Invalid about key "
+
str
(
section_key
))
raise
KeyError
(
"Invalid about key "
+
str
(
section_key
))
...
@@ -366,22 +373,6 @@ def get_course_syllabus_section(course, section_key):
...
@@ -366,22 +373,6 @@ def get_course_syllabus_section(course, section_key):
raise
KeyError
(
"Invalid about key "
+
str
(
section_key
))
raise
KeyError
(
"Invalid about key "
+
str
(
section_key
))
def
get_courses_by_university
(
user
,
domain
=
None
):
'''
Returns dict of lists of courses available, keyed by course.org (ie university).
Courses are sorted by course.number.
'''
# TODO: Clean up how 'error' is done.
# filter out any courses that errored.
visible_courses
=
get_courses
(
user
,
domain
)
universities
=
defaultdict
(
list
)
for
course
in
visible_courses
:
universities
[
course
.
org
]
.
append
(
course
)
return
universities
def
get_courses
(
user
,
domain
=
None
):
def
get_courses
(
user
,
domain
=
None
):
'''
'''
Returns a list of courses available, sorted by course.number
Returns a list of courses available, sorted by course.number
...
@@ -400,6 +391,16 @@ def get_courses(user, domain=None):
...
@@ -400,6 +391,16 @@ def get_courses(user, domain=None):
return
courses
return
courses
def
get_permission_for_course_about
():
"""
Returns the CourseOverview object for the course after checking for access.
"""
return
microsite
.
get_value
(
'COURSE_ABOUT_VISIBILITY_PERMISSION'
,
settings
.
COURSE_ABOUT_VISIBILITY_PERMISSION
)
def
sort_by_announcement
(
courses
):
def
sort_by_announcement
(
courses
):
"""
"""
Sorts a list of courses by their announcement date. If the date is
Sorts a list of courses by their announcement date. If the date is
...
...
lms/djangoapps/courseware/tests/helpers.py
View file @
2b8441a0
...
@@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse
...
@@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse
from
django.test
import
TestCase
from
django.test
import
TestCase
from
django.test.client
import
RequestFactory
from
django.test.client
import
RequestFactory
from
courseware.access
import
has_access
,
COURSE_OVERVIEW_SUPPORTED_ACTIONS
from
courseware.access
import
has_access
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
student.models
import
Registration
from
student.models
import
Registration
...
@@ -151,30 +151,27 @@ class CourseAccessTestMixin(TestCase):
...
@@ -151,30 +151,27 @@ class CourseAccessTestMixin(TestCase):
"""
"""
Assert that a user has access to the given action for a given course.
Assert that a user has access to the given action for a given course.
Test with both the given course and
, if the action is supported, with
Test with both the given course and
with a CourseOverview of the given
a CourseOverview of the given
course.
course.
Arguments:
Arguments:
user (User): a user.
user (User): a user.
action (str): type of access to test.
action (str): type of access to test.
See access.py:COURSE_OVERVIEW_SUPPORTED_ACTIONS.
course (CourseDescriptor): a course.
course (CourseDescriptor): a course.
"""
"""
self
.
assertTrue
(
has_access
(
user
,
action
,
course
))
self
.
assertTrue
(
has_access
(
user
,
action
,
course
))
if
action
in
COURSE_OVERVIEW_SUPPORTED_ACTIONS
:
self
.
assertTrue
(
has_access
(
user
,
action
,
CourseOverview
.
get_from_id
(
course
.
id
)))
self
.
assertTrue
(
has_access
(
user
,
action
,
CourseOverview
.
get_from_id
(
course
.
id
)))
def
assertCannotAccessCourse
(
self
,
user
,
action
,
course
):
def
assertCannotAccessCourse
(
self
,
user
,
action
,
course
):
"""
"""
Assert that a user lacks access to the given action the given course.
Assert that a user lacks access to the given action the given course.
Test with both the given course and
, if the action is supported, with
Test with both the given course and
with a CourseOverview of the given
a CourseOverview of the given
course.
course.
Arguments:
Arguments:
user (User): a user.
user (User): a user.
action (str): type of access to test.
action (str): type of access to test.
See access.py:COURSE_OVERVIEW_SUPPORTED_ACTIONS.
course (CourseDescriptor): a course.
course (CourseDescriptor): a course.
Note:
Note:
...
@@ -184,5 +181,4 @@ class CourseAccessTestMixin(TestCase):
...
@@ -184,5 +181,4 @@ class CourseAccessTestMixin(TestCase):
stack traces of failed tests easier to understand at a glance.
stack traces of failed tests easier to understand at a glance.
"""
"""
self
.
assertFalse
(
has_access
(
user
,
action
,
course
))
self
.
assertFalse
(
has_access
(
user
,
action
,
course
))
if
action
in
COURSE_OVERVIEW_SUPPORTED_ACTIONS
:
self
.
assertFalse
(
has_access
(
user
,
action
,
CourseOverview
.
get_from_id
(
course
.
id
)))
self
.
assertFalse
(
has_access
(
user
,
action
,
CourseOverview
.
get_from_id
(
course
.
id
)))
lms/djangoapps/courseware/tests/test_access.py
View file @
2b8441a0
...
@@ -236,7 +236,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
...
@@ -236,7 +236,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
mock_unit
.
start
=
start
mock_unit
.
start
=
start
self
.
verify_access
(
mock_unit
,
expected_access
,
expected_error_type
)
self
.
verify_access
(
mock_unit
,
expected_access
,
expected_error_type
)
def
test__has_access_course_
desc_
can_enroll
(
self
):
def
test__has_access_course_can_enroll
(
self
):
yesterday
=
datetime
.
datetime
.
now
(
pytz
.
utc
)
-
datetime
.
timedelta
(
days
=
1
)
yesterday
=
datetime
.
datetime
.
now
(
pytz
.
utc
)
-
datetime
.
timedelta
(
days
=
1
)
tomorrow
=
datetime
.
datetime
.
now
(
pytz
.
utc
)
+
datetime
.
timedelta
(
days
=
1
)
tomorrow
=
datetime
.
datetime
.
now
(
pytz
.
utc
)
+
datetime
.
timedelta
(
days
=
1
)
...
@@ -248,11 +248,11 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
...
@@ -248,11 +248,11 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
enrollment_domain
=
''
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
enrollment_domain
=
''
)
)
CourseEnrollmentAllowedFactory
(
email
=
user
.
email
,
course_id
=
course
.
id
)
CourseEnrollmentAllowedFactory
(
email
=
user
.
email
,
course_id
=
course
.
id
)
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
user
,
'enroll'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
user
,
'enroll'
,
course
))
# Staff can always enroll even outside the open enrollment period
# Staff can always enroll even outside the open enrollment period
user
=
StaffFactory
.
create
(
course_key
=
course
.
id
)
user
=
StaffFactory
.
create
(
course_key
=
course
.
id
)
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
user
,
'enroll'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
user
,
'enroll'
,
course
))
# Non-staff cannot enroll if it is between the start and end dates and invitation only
# Non-staff cannot enroll if it is between the start and end dates and invitation only
# and not specifically allowed
# and not specifically allowed
...
@@ -262,7 +262,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
...
@@ -262,7 +262,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
invitation_only
=
True
invitation_only
=
True
)
)
user
=
UserFactory
.
create
()
user
=
UserFactory
.
create
()
self
.
assertFalse
(
access
.
_has_access_course
_desc
(
user
,
'enroll'
,
course
))
self
.
assertFalse
(
access
.
_has_access_course
(
user
,
'enroll'
,
course
))
# Non-staff can enroll if it is between the start and end dates and not invitation only
# Non-staff can enroll if it is between the start and end dates and not invitation only
course
=
Mock
(
course
=
Mock
(
...
@@ -270,7 +270,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
...
@@ -270,7 +270,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
enrollment_domain
=
''
,
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
enrollment_domain
=
''
,
invitation_only
=
False
invitation_only
=
False
)
)
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
user
,
'enroll'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
user
,
'enroll'
,
course
))
# Non-staff cannot enroll outside the open enrollment period if not specifically allowed
# Non-staff cannot enroll outside the open enrollment period if not specifically allowed
course
=
Mock
(
course
=
Mock
(
...
@@ -278,7 +278,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
...
@@ -278,7 +278,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
enrollment_domain
=
''
,
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
enrollment_domain
=
''
,
invitation_only
=
False
invitation_only
=
False
)
)
self
.
assertFalse
(
access
.
_has_access_course
_desc
(
user
,
'enroll'
,
course
))
self
.
assertFalse
(
access
.
_has_access_course
(
user
,
'enroll'
,
course
))
def
test__user_passed_as_none
(
self
):
def
test__user_passed_as_none
(
self
):
"""Ensure has_access handles a user being passed as null"""
"""Ensure has_access handles a user being passed as null"""
...
@@ -296,40 +296,30 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
...
@@ -296,40 +296,30 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
id
=
course_id
,
id
=
course_id
,
catalog_visibility
=
CATALOG_VISIBILITY_CATALOG_AND_ABOUT
catalog_visibility
=
CATALOG_VISIBILITY_CATALOG_AND_ABOUT
)
)
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
user
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
user
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
user
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
user
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
staff
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
staff
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'see_about_page'
,
course
))
# Now set visibility to just about page
# Now set visibility to just about page
course
=
Mock
(
course
=
Mock
(
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
catalog_visibility
=
CATALOG_VISIBILITY_ABOUT
catalog_visibility
=
CATALOG_VISIBILITY_ABOUT
)
)
self
.
assertFalse
(
access
.
_has_access_course
_desc
(
user
,
'see_in_catalog'
,
course
))
self
.
assertFalse
(
access
.
_has_access_course
(
user
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
user
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
user
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
staff
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
staff
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'see_about_page'
,
course
))
# Now set visibility to none, which means neither in catalog nor about pages
# Now set visibility to none, which means neither in catalog nor about pages
course
=
Mock
(
course
=
Mock
(
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
catalog_visibility
=
CATALOG_VISIBILITY_NONE
catalog_visibility
=
CATALOG_VISIBILITY_NONE
)
)
self
.
assertFalse
(
access
.
_has_access_course_desc
(
user
,
'see_in_catalog'
,
course
))
self
.
assertFalse
(
access
.
_has_access_course
(
user
,
'see_in_catalog'
,
course
))
self
.
assertFalse
(
access
.
_has_access_course_desc
(
user
,
'see_about_page'
,
course
))
self
.
assertFalse
(
access
.
_has_access_course
(
user
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course_desc
(
staff
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course_desc
(
staff
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'see_about_page'
,
course
))
@ddt.data
(
True
,
False
)
@patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ACCESS_REQUIRE_STAFF_FOR_COURSE'
:
True
})
def
test_see_exists
(
self
,
ispublic
):
"""
Test if user can see course
"""
user
=
UserFactory
.
create
(
is_staff
=
False
)
course
=
Mock
(
ispublic
=
ispublic
)
self
.
assertEquals
(
bool
(
access
.
_has_access_course_desc
(
user
,
'see_exists'
,
course
)),
ispublic
)
@patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENABLE_PREREQUISITE_COURSES'
:
True
,
'MILESTONES_APP'
:
True
})
@patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENABLE_PREREQUISITE_COURSES'
:
True
,
'MILESTONES_APP'
:
True
})
def
test_access_on_course_with_pre_requisites
(
self
):
def
test_access_on_course_with_pre_requisites
(
self
):
...
@@ -351,16 +341,16 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
...
@@ -351,16 +341,16 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
# user should not be able to load course even if enrolled
# user should not be able to load course even if enrolled
CourseEnrollmentFactory
(
user
=
user
,
course_id
=
course
.
id
)
CourseEnrollmentFactory
(
user
=
user
,
course_id
=
course
.
id
)
response
=
access
.
_has_access_course
_desc
(
user
,
'view_courseware_with_prerequisites'
,
course
)
response
=
access
.
_has_access_course
(
user
,
'view_courseware_with_prerequisites'
,
course
)
self
.
assertFalse
(
response
)
self
.
assertFalse
(
response
)
self
.
assertIsInstance
(
response
,
access_response
.
MilestoneError
)
self
.
assertIsInstance
(
response
,
access_response
.
MilestoneError
)
# Staff can always access course
# Staff can always access course
staff
=
StaffFactory
.
create
(
course_key
=
course
.
id
)
staff
=
StaffFactory
.
create
(
course_key
=
course
.
id
)
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
staff
,
'view_courseware_with_prerequisites'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'view_courseware_with_prerequisites'
,
course
))
# User should be able access after completing required course
# User should be able access after completing required course
fulfill_course_milestone
(
pre_requisite_course
.
id
,
user
)
fulfill_course_milestone
(
pre_requisite_course
.
id
,
user
)
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
user
,
'view_courseware_with_prerequisites'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
user
,
'view_courseware_with_prerequisites'
,
course
))
@ddt.data
(
@ddt.data
(
(
True
,
True
,
True
),
(
True
,
True
,
True
),
...
@@ -377,10 +367,10 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
...
@@ -377,10 +367,10 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
descriptor
.
mobile_available
=
mobile_available
descriptor
.
mobile_available
=
mobile_available
self
.
assertEqual
(
self
.
assertEqual
(
bool
(
access
.
_has_access_course
_desc
(
self
.
student
,
'load_mobile'
,
descriptor
)),
bool
(
access
.
_has_access_course
(
self
.
student
,
'load_mobile'
,
descriptor
)),
student_expected
student_expected
)
)
self
.
assertEqual
(
bool
(
access
.
_has_access_course
_desc
(
self
.
staff
,
'load_mobile'
,
descriptor
)),
staff_expected
)
self
.
assertEqual
(
bool
(
access
.
_has_access_course
(
self
.
staff
,
'load_mobile'
,
descriptor
)),
staff_expected
)
@patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENABLE_PREREQUISITE_COURSES'
:
True
,
'MILESTONES_APP'
:
True
})
@patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENABLE_PREREQUISITE_COURSES'
:
True
,
'MILESTONES_APP'
:
True
})
def
test_courseware_page_unfulfilled_prereqs
(
self
):
def
test_courseware_page_unfulfilled_prereqs
(
self
):
...
@@ -552,7 +542,6 @@ class CourseOverviewAccessTestCase(ModuleStoreTestCase):
...
@@ -552,7 +542,6 @@ class CourseOverviewAccessTestCase(ModuleStoreTestCase):
user_attr_name (str): the name of the attribute on self that is the
user_attr_name (str): the name of the attribute on self that is the
User to test with.
User to test with.
action (str): action to test with.
action (str): action to test with.
See COURSE_OVERVIEW_SUPPORTED_ACTIONS for valid values.
course_attr_name (str): the name of the attribute on self that is
course_attr_name (str): the name of the attribute on self that is
the CourseDescriptor to test with.
the CourseDescriptor to test with.
"""
"""
...
...
lms/djangoapps/courseware/tests/test_courses.py
View file @
2b8441a0
...
@@ -18,7 +18,7 @@ from courseware.courses import (
...
@@ -18,7 +18,7 @@ from courseware.courses import (
get_course_info_section
,
get_course_about_section
,
get_cms_block_link
get_course_info_section
,
get_course_about_section
,
get_cms_block_link
)
)
from
courseware.courses
import
get_course_with_access
from
courseware.courses
import
get_course_with_access
,
get_course_overview_with_access
from
courseware.module_render
import
get_module_for_descriptor
from
courseware.module_render
import
get_module_for_descriptor
from
courseware.tests.helpers
import
get_request_for_user
from
courseware.tests.helpers
import
get_request_for_user
from
courseware.model_data
import
FieldDataCache
from
courseware.model_data
import
FieldDataCache
...
@@ -30,7 +30,7 @@ from xmodule.modulestore import ModuleStoreEnum
...
@@ -30,7 +30,7 @@ from xmodule.modulestore import ModuleStoreEnum
from
xmodule.modulestore.xml_importer
import
import_course_from_xml
from
xmodule.modulestore.xml_importer
import
import_course_from_xml
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.django_utils
import
TEST_DATA_MIXED_TOY_MODULESTORE
from
xmodule.modulestore.tests.django_utils
import
TEST_DATA_MIXED_TOY_MODULESTORE
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
,
check_mongo_calls
from
xmodule.tests.xml
import
factories
as
xml
from
xmodule.tests.xml
import
factories
as
xml
from
xmodule.tests.xml
import
XModuleXmlImportTest
from
xmodule.tests.xml
import
XModuleXmlImportTest
...
@@ -40,6 +40,7 @@ TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
...
@@ -40,6 +40,7 @@ TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
@attr
(
'shard_1'
)
@attr
(
'shard_1'
)
@ddt.ddt
class
CoursesTest
(
ModuleStoreTestCase
):
class
CoursesTest
(
ModuleStoreTestCase
):
"""Test methods related to fetching courses."""
"""Test methods related to fetching courses."""
...
@@ -57,16 +58,28 @@ class CoursesTest(ModuleStoreTestCase):
...
@@ -57,16 +58,28 @@ class CoursesTest(ModuleStoreTestCase):
cms_url
=
u"//{}/course/{}"
.
format
(
CMS_BASE_TEST
,
unicode
(
self
.
course
.
location
))
cms_url
=
u"//{}/course/{}"
.
format
(
CMS_BASE_TEST
,
unicode
(
self
.
course
.
location
))
self
.
assertEqual
(
cms_url
,
get_cms_block_link
(
self
.
course
,
'course'
))
self
.
assertEqual
(
cms_url
,
get_cms_block_link
(
self
.
course
,
'course'
))
def
test_get_course_with_access
(
self
):
@ddt.data
(
get_course_with_access
,
get_course_overview_with_access
)
def
test_get_course_func_with_access_error
(
self
,
course_access_func
):
user
=
UserFactory
.
create
()
user
=
UserFactory
.
create
()
course
=
CourseFactory
.
create
(
visible_to_staff_only
=
True
)
course
=
CourseFactory
.
create
(
visible_to_staff_only
=
True
)
with
self
.
assertRaises
(
CoursewareAccessException
)
as
error
:
with
self
.
assertRaises
(
CoursewareAccessException
)
as
error
:
get_course_with_access
(
user
,
'load'
,
course
.
id
)
course_access_func
(
user
,
'load'
,
course
.
id
)
self
.
assertEqual
(
error
.
exception
.
message
,
"Course not found."
)
self
.
assertEqual
(
error
.
exception
.
message
,
"Course not found."
)
self
.
assertEqual
(
error
.
exception
.
access_response
.
error_code
,
"not_visible_to_user"
)
self
.
assertEqual
(
error
.
exception
.
access_response
.
error_code
,
"not_visible_to_user"
)
self
.
assertFalse
(
error
.
exception
.
access_response
.
has_access
)
self
.
assertFalse
(
error
.
exception
.
access_response
.
has_access
)
@ddt.data
(
(
get_course_with_access
,
1
),
(
get_course_overview_with_access
,
0
),
)
@ddt.unpack
def
test_get_course_func_with_access
(
self
,
course_access_func
,
num_mongo_calls
):
user
=
UserFactory
.
create
()
course
=
CourseFactory
.
create
(
emit_signals
=
True
)
with
check_mongo_calls
(
num_mongo_calls
):
course_access_func
(
user
,
'load'
,
course
.
id
)
@attr
(
'shard_1'
)
@attr
(
'shard_1'
)
class
ModuleStoreBranchSettingTest
(
ModuleStoreTestCase
):
class
ModuleStoreBranchSettingTest
(
ModuleStoreTestCase
):
...
...
lms/djangoapps/courseware/views.py
View file @
2b8441a0
...
@@ -37,11 +37,17 @@ from courseware.access import has_access, _adjust_start_date_for_beta_testers
...
@@ -37,11 +37,17 @@ from courseware.access import has_access, _adjust_start_date_for_beta_testers
from
courseware.access_response
import
StartDateError
from
courseware.access_response
import
StartDateError
from
courseware.access_utils
import
in_preview_mode
from
courseware.access_utils
import
in_preview_mode
from
courseware.courses
import
(
from
courseware.courses
import
(
get_courses
,
get_course
,
get_course_by_id
,
get_courses
,
get_studio_url
,
get_course_with_access
,
get_course
,
get_course_by_id
,
get_permission_for_course_about
,
get_studio_url
,
get_course_overview_with_access
,
get_course_with_access
,
sort_by_announcement
,
sort_by_announcement
,
sort_by_start_date
,
sort_by_start_date
,
UserNotEnrolled
)
UserNotEnrolled
)
from
courseware.masquerade
import
setup_masquerade
from
courseware.masquerade
import
setup_masquerade
from
openedx.core.djangoapps.credit.api
import
(
from
openedx.core.djangoapps.credit.api
import
(
get_credit_requirement_status
,
get_credit_requirement_status
,
...
@@ -802,11 +808,8 @@ def course_about(request, course_id):
...
@@ -802,11 +808,8 @@ def course_about(request, course_id):
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
with
modulestore
()
.
bulk_operations
(
course_key
):
with
modulestore
()
.
bulk_operations
(
course_key
):
permission_name
=
microsite
.
get_value
(
permission
=
get_permission_for_course_about
()
'COURSE_ABOUT_VISIBILITY_PERMISSION'
,
course
=
get_course_with_access
(
request
.
user
,
permission
,
course_key
)
settings
.
COURSE_ABOUT_VISIBILITY_PERMISSION
)
course
=
get_course_with_access
(
request
.
user
,
permission_name
,
course_key
)
if
microsite
.
get_value
(
'ENABLE_MKTG_SITE'
,
settings
.
FEATURES
.
get
(
'ENABLE_MKTG_SITE'
,
False
)):
if
microsite
.
get_value
(
'ENABLE_MKTG_SITE'
,
settings
.
FEATURES
.
get
(
'ENABLE_MKTG_SITE'
,
False
)):
return
redirect
(
reverse
(
'info'
,
args
=
[
course
.
id
.
to_deprecated_string
()]))
return
redirect
(
reverse
(
'info'
,
args
=
[
course
.
id
.
to_deprecated_string
()]))
...
@@ -1066,7 +1069,7 @@ def submission_history(request, course_id, student_username, location):
...
@@ -1066,7 +1069,7 @@ def submission_history(request, course_id, student_username, location):
except
(
InvalidKeyError
,
AssertionError
):
except
(
InvalidKeyError
,
AssertionError
):
return
HttpResponse
(
escape
(
_
(
u'Invalid location.'
)))
return
HttpResponse
(
escape
(
_
(
u'Invalid location.'
)))
course
=
get_course_with_access
(
request
.
user
,
'load'
,
course_key
)
course
=
get_course_
overview_
with_access
(
request
.
user
,
'load'
,
course_key
)
staff_access
=
bool
(
has_access
(
request
.
user
,
'staff'
,
course
))
staff_access
=
bool
(
has_access
(
request
.
user
,
'staff'
,
course
))
# Permission Denied if they don't have staff access and are trying to see
# Permission Denied if they don't have staff access and are trying to see
...
...
lms/djangoapps/django_comment_client/base/views.py
View file @
2b8441a0
...
@@ -15,7 +15,7 @@ from opaque_keys.edx.keys import CourseKey
...
@@ -15,7 +15,7 @@ from opaque_keys.edx.keys import CourseKey
from
courseware.access
import
has_access
from
courseware.access
import
has_access
from
util.file
import
store_uploaded_file
from
util.file
import
store_uploaded_file
from
courseware.courses
import
get_course_with_access
,
get_course_by_id
from
courseware.courses
import
get_course_with_access
,
get_course_
overview_with_access
,
get_course_
by_id
import
django_comment_client.settings
as
cc_settings
import
django_comment_client.settings
as
cc_settings
from
django_comment_common.signals
import
(
from
django_comment_common.signals
import
(
thread_created
,
thread_created
,
...
@@ -770,7 +770,7 @@ def users(request, course_id):
...
@@ -770,7 +770,7 @@ def users(request, course_id):
course_key
=
CourseKey
.
from_string
(
course_id
)
course_key
=
CourseKey
.
from_string
(
course_id
)
try
:
try
:
get_course_with_access
(
request
.
user
,
'load'
,
course_key
,
check_if_enrolled
=
True
)
get_course_
overview_
with_access
(
request
.
user
,
'load'
,
course_key
,
check_if_enrolled
=
True
)
except
Http404
:
except
Http404
:
# course didn't exist, or requesting user does not have access to it.
# course didn't exist, or requesting user does not have access to it.
return
JsonError
(
status
=
404
)
return
JsonError
(
status
=
404
)
...
...
lms/templates/course.html
View file @
2b8441a0
...
@@ -2,30 +2,28 @@
...
@@ -2,30 +2,28 @@
<
%!
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
core
.
urlresolvers
import
reverse
from
django
.
core
.
urlresolvers
import
reverse
from
courseware
.
courses
import
get_course_about_section
from
openedx
.
core
.
lib
.
courses
import
course_image_url
%
>
%
>
<
%
page
args=
"course"
/>
<
%
page
args=
"course"
/>
<article
class=
"course"
id=
"${course.id | h}"
role=
"region"
aria-label=
"${
get_course_about_section(request, course, 'title')
}"
>
<article
class=
"course"
id=
"${course.id | h}"
role=
"region"
aria-label=
"${
course.display_name_with_default
}"
>
<a
href=
"${reverse('about_course', args=[course.id.to_deprecated_string()])}"
>
<a
href=
"${reverse('about_course', args=[course.id.to_deprecated_string()])}"
>
<header
class=
"course-image"
>
<header
class=
"course-image"
>
<div
class=
"cover-image"
>
<div
class=
"cover-image"
>
<img
src=
"${course
_image_url(course)}"
alt=
"${get_course_about_section(request, course, 'title')} ${course.display_number_with_default
}"
/>
<img
src=
"${course
.course_image_url | h}"
alt=
"${course.display_name_with_default} ${course.display_number_with_default | h
}"
/>
<div
class=
"learn-more"
aria-hidden=
true
>
${_("LEARN MORE")}
</div>
<div
class=
"learn-more"
aria-hidden=
true
>
${_("LEARN MORE")}
</div>
</div>
</div>
</header>
</header>
<div
class=
"course-info"
aria-hidden=
"true"
>
<div
class=
"course-info"
aria-hidden=
"true"
>
<h2
class=
"course-name"
>
<h2
class=
"course-name"
>
<span
class=
"course-organization"
>
${
get_course_about_section(request, course, 'university')
}
</span>
<span
class=
"course-organization"
>
${
course.display_org_with_default | h
}
</span>
<span
class=
"course-code"
>
${course.display_number_with_default}
</span>
<span
class=
"course-code"
>
${course.display_number_with_default
| h
}
</span>
<span
class=
"course-title"
>
${
get_course_about_section(request, course, 'title')
}
</span>
<span
class=
"course-title"
>
${
course.display_name_with_default
}
</span>
</h2>
</h2>
<div
class=
"course-date"
aria-hidden=
"true"
>
${_("Starts")}: ${course.start_datetime_text()}
</div>
<div
class=
"course-date"
aria-hidden=
"true"
>
${_("Starts")}: ${course.start_datetime_text()}
</div>
</div>
</div>
<div
class=
"sr"
>
<div
class=
"sr"
>
<ul>
<ul>
<li>
${
get_course_about_section(request, course, 'university')
}
</li>
<li>
${
course.display_org_with_default | h
}
</li>
<li>
${course.display_number_with_default}
</li>
<li>
${course.display_number_with_default
| h
}
</li>
<li>
${_("Starts")}:
<time
itemprop=
"startDate"
datetime=
"${course.start_datetime_text()}"
>
${course.start_datetime_text()}
</time></li>
<li>
${_("Starts")}:
<time
itemprop=
"startDate"
datetime=
"${course.start_datetime_text()}"
>
${course.start_datetime_text()}
</time></li>
</ul>
</ul>
</div>
</div>
...
...
lms/templates/courseware/course_about.html
View file @
2b8441a0
...
@@ -13,7 +13,7 @@ from openedx.core.lib.courses import course_image_url
...
@@ -13,7 +13,7 @@ from openedx.core.lib.courses import course_image_url
<
%
block
name=
"headextra"
>
<
%
block
name=
"headextra"
>
## OG (Open Graph) title and description added below to give social media info to display
## OG (Open Graph) title and description added below to give social media info to display
## (https://developers.facebook.com/docs/opengraph/howtos/maximizing-distribution-media-content#tags)
## (https://developers.facebook.com/docs/opengraph/howtos/maximizing-distribution-media-content#tags)
<meta
property=
"og:title"
content=
"${
get_course_about_section(request, course, 'title')
}"
/>
<meta
property=
"og:title"
content=
"${
course.display_name_with_default
}"
/>
<meta
property=
"og:description"
content=
"${get_course_about_section(request, course, 'short_description')}"
/>
<meta
property=
"og:description"
content=
"${get_course_about_section(request, course, 'short_description')}"
/>
</
%
block>
</
%
block>
...
@@ -102,7 +102,7 @@ from openedx.core.lib.courses import course_image_url
...
@@ -102,7 +102,7 @@ from openedx.core.lib.courses import course_image_url
<script
src=
"${static.url('js/course_info.js')}"
></script>
<script
src=
"${static.url('js/course_info.js')}"
></script>
</
%
block>
</
%
block>
<
%
block
name=
"pagetitle"
>
${
get_course_about_section(request, course, "title")
}
</
%
block>
<
%
block
name=
"pagetitle"
>
${
course.display_name_with_default
}
</
%
block>
<section
class=
"course-info"
>
<section
class=
"course-info"
>
<header
class=
"course-profile"
>
<header
class=
"course-profile"
>
...
@@ -111,9 +111,9 @@ from openedx.core.lib.courses import course_image_url
...
@@ -111,9 +111,9 @@ from openedx.core.lib.courses import course_image_url
<section
class=
"intro"
>
<section
class=
"intro"
>
<hgroup>
<hgroup>
<h1>
<h1>
${
get_course_about_section(request, course, "title")
}
${
course.display_name_with_default
}
% if not self.theme_enabled():
% if not self.theme_enabled():
<a
href=
"#"
>
${
get_course_about_section(request, course, "university")
}
</a>
<a
href=
"#"
>
${
course.display_org_with_default | h
}
</a>
% endif
% endif
</h1>
</h1>
</hgroup>
</hgroup>
...
@@ -220,10 +220,10 @@ from openedx.core.lib.courses import course_image_url
...
@@ -220,10 +220,10 @@ from openedx.core.lib.courses import course_image_url
## or something allowing themes to do whatever they
## or something allowing themes to do whatever they
## want here (and on this whole page, really).
## want here (and on this whole page, really).
% if self.stanford_theme_enabled():
% if self.stanford_theme_enabled():
<a
href=
"http://twitter.com/intent/tweet?text=I+just+enrolled+in+${course.number}+${
get_course_about_section(request, course, 'title')
}!+(http://class.stanford.edu)"
class=
"share"
>
<a
href=
"http://twitter.com/intent/tweet?text=I+just+enrolled+in+${course.number}+${
course.display_name_with_default
}!+(http://class.stanford.edu)"
class=
"share"
>
<i
class=
"icon fa fa-twitter"
></i><span
class=
"sr"
>
${_("Tweet that you've enrolled in this course")}
</span>
<i
class=
"icon fa fa-twitter"
></i><span
class=
"sr"
>
${_("Tweet that you've enrolled in this course")}
</span>
</a>
</a>
<a
href=
"mailto:?subject=Take%20a%20course%20at%20Stanford%20online!&body=I%20just%20enrolled%20in%20${course.number}%20${
get_course_about_section(request, course, 'title')
}+(http://class.stanford.edu)"
class=
"share"
>
<a
href=
"mailto:?subject=Take%20a%20course%20at%20Stanford%20online!&body=I%20just%20enrolled%20in%20${course.number}%20${
course.display_name_with_default
}+(http://class.stanford.edu)"
class=
"share"
>
<i
class=
"icon fa fa-envelope"
></i><span
class=
"sr"
>
${_("Email someone to say you've enrolled in this course")}
</span>
<i
class=
"icon fa fa-envelope"
></i><span
class=
"sr"
>
${_("Email someone to say you've enrolled in this course")}
</span>
</a>
</a>
% else:
% else:
...
@@ -235,7 +235,7 @@ from openedx.core.lib.courses import course_image_url
...
@@ -235,7 +235,7 @@ from openedx.core.lib.courses import course_image_url
##
Twitter
account
.
{
url
}
should
appear
at
the
end
of
the
text
.
##
Twitter
account
.
{
url
}
should
appear
at
the
end
of
the
text
.
tweet_text =
_("I
just
enrolled
in
{
number
}
{
title
}
through
{
account
}
:
{
url
}").
format
(
tweet_text =
_("I
just
enrolled
in
{
number
}
{
title
}
through
{
account
}
:
{
url
}").
format
(
number=
course.number,
number=
course.number,
title=
get_course_about_section(request,
course
,
'
title
')
,
title=
course.display_name_with_default
,
account=
microsite.get_value('course_about_twitter_account',
settings
.
PLATFORM_TWITTER_ACCOUNT
),
account=
microsite.get_value('course_about_twitter_account',
settings
.
PLATFORM_TWITTER_ACCOUNT
),
url=
u"http://{domain}{path}".format(
url=
u"http://{domain}{path}".format(
domain=
site_domain,
domain=
site_domain,
...
@@ -250,7 +250,7 @@ from openedx.core.lib.courses import course_image_url
...
@@ -250,7 +250,7 @@ from openedx.core.lib.courses import course_image_url
subject=
_("Take
a
course
with
{
platform
}
online
").
format
(
platform=
platform_name),
subject=
_("Take
a
course
with
{
platform
}
online
").
format
(
platform=
platform_name),
body=
_("I
just
enrolled
in
{
number
}
{
title
}
through
{
platform
}
{
url
}").
format
(
body=
_("I
just
enrolled
in
{
number
}
{
title
}
through
{
platform
}
{
url
}").
format
(
number=
course.number,
number=
course.number,
title=
get_course_about_section(request,
course
,
'
title
')
,
title=
course.display_name_with_default
,
platform=
platform_name,
platform=
platform_name,
url=
u"http://{domain}{path}".format(
url=
u"http://{domain}{path}".format(
domain=
site_domain,
domain=
site_domain,
...
...
lms/templates/dashboard/_dashboard_course_listing.html
View file @
2b8441a0
...
@@ -7,7 +7,6 @@ from django.utils.translation import ugettext as _
...
@@ -7,7 +7,6 @@ from django.utils.translation import ugettext as _
from
django
.
utils
.
translation
import
ungettext
from
django
.
utils
.
translation
import
ungettext
from
django
.
core
.
urlresolvers
import
reverse
from
django
.
core
.
urlresolvers
import
reverse
from
markupsafe
import
escape
from
markupsafe
import
escape
from
courseware
.
courses
import
get_course_university_about_section
from
course_modes
.
models
import
CourseMode
from
course_modes
.
models
import
CourseMode
from
course_modes
.
helpers
import
enrollment_mode_display
from
course_modes
.
helpers
import
enrollment_mode_display
from
student
.
helpers
import
(
from
student
.
helpers
import
(
...
@@ -99,7 +98,7 @@ from student.helpers import (
...
@@ -99,7 +98,7 @@ from student.helpers import (
% endif
% endif
</h3>
</h3>
<div
class=
"course-info"
>
<div
class=
"course-info"
>
<span
class=
"info-university"
>
${
get_course_university_about_section(course_overview)
} -
</span>
<span
class=
"info-university"
>
${
course_overview.display_org_with_default | h
} -
</span>
<span
class=
"info-course-id"
>
${course_overview.display_number_with_default | h}
</span>
<span
class=
"info-course-id"
>
${course_overview.display_number_with_default | h}
</span>
<span
class=
"info-date-block"
data-tooltip=
"Hi"
>
<span
class=
"info-date-block"
data-tooltip=
"Hi"
>
% if course_overview.has_ended():
% if course_overview.has_ended():
...
...
lms/templates/shoppingcart/receipt.html
View file @
2b8441a0
...
@@ -3,7 +3,6 @@
...
@@ -3,7 +3,6 @@
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
utils
.
translation
import
ungettext
from
django
.
utils
.
translation
import
ungettext
from
django
.
core
.
urlresolvers
import
reverse
from
django
.
core
.
urlresolvers
import
reverse
from
courseware
.
courses
import
get_course_about_section
,
get_course_by_id
from
markupsafe
import
escape
from
markupsafe
import
escape
from
microsite_configuration
import
microsite
from
microsite_configuration
import
microsite
from
openedx
.
core
.
lib
.
courses
import
course_image_url
from
openedx
.
core
.
lib
.
courses
import
course_image_url
...
@@ -293,7 +292,7 @@ from openedx.core.lib.courses import course_image_url
...
@@ -293,7 +292,7 @@ from openedx.core.lib.courses import course_image_url
<div
class=
"clearfix"
>
<div
class=
"clearfix"
>
<div
class=
"image"
>
<div
class=
"image"
>
<img
class=
"item-image"
src=
"${course_image_url(course)}"
<img
class=
"item-image"
src=
"${course_image_url(course)}"
alt=
"${course.display_number_with_default | h} ${
get_course_about_section(request, course, 'title')
} Image"
/>
alt=
"${course.display_number_with_default | h} ${
course.display_name_with_default
} Image"
/>
</div>
</div>
<div
class=
"data-input"
>
<div
class=
"data-input"
>
...
...
lms/templates/shoppingcart/registration_code_receipt.html
View file @
2b8441a0
<
%!
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
core
.
urlresolvers
import
reverse
from
django
.
core
.
urlresolvers
import
reverse
from
courseware
.
courses
import
get_course_about_section
from
openedx
.
core
.
lib
.
courses
import
course_image_url
from
openedx
.
core
.
lib
.
courses
import
course_image_url
%
>
%
>
<
%
inherit
file=
"../main.html"
/>
<
%
inherit
file=
"../main.html"
/>
...
@@ -21,7 +20,7 @@ from openedx.core.lib.courses import course_image_url
...
@@ -21,7 +20,7 @@ from openedx.core.lib.courses import course_image_url
<img
class=
"item-image"
src=
"${course_image_url(course)}"
<img
class=
"item-image"
src=
"${course_image_url(course)}"
alt=
"${_("
{
course_number
}
{
course_title
}
Cover
Image
").
format
(
alt=
"${_("
{
course_number
}
{
course_title
}
Cover
Image
").
format
(
course_number=
course.display_number_with_default,
course_number=
course.display_number_with_default,
course_title=
get_course_about_section(request,
course
,
'
title
')
,
course_title=
course.display_name_with_default
,
)}"
/>
)}"
/>
</div>
</div>
<div
class=
"enrollment-details"
>
<div
class=
"enrollment-details"
>
...
...
lms/templates/shoppingcart/registration_code_redemption.html
View file @
2b8441a0
<
%!
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
core
.
urlresolvers
import
reverse
from
django
.
core
.
urlresolvers
import
reverse
from
courseware
.
courses
import
get_course_about_section
from
openedx
.
core
.
lib
.
courses
import
course_image_url
from
openedx
.
core
.
lib
.
courses
import
course_image_url
%
>
%
>
<
%
inherit
file=
"../main.html"
/>
<
%
inherit
file=
"../main.html"
/>
...
@@ -21,7 +20,7 @@ from openedx.core.lib.courses import course_image_url
...
@@ -21,7 +20,7 @@ from openedx.core.lib.courses import course_image_url
<img
class=
"item-image"
src=
"${course_image_url(course)}"
<img
class=
"item-image"
src=
"${course_image_url(course)}"
alt=
"${_("
{
course_number
}
{
course_title
}
Cover
Image
").
format
(
alt=
"${_("
{
course_number
}
{
course_title
}
Cover
Image
").
format
(
course_number=
course.display_number_with_default,
course_number=
course.display_number_with_default,
course_title=
get_course_about_section(request,
course
,
'
title
')
,
course_title=
course.display_name_with_default
,
)}"
/>
)}"
/>
</div>
</div>
<div
class=
"enrollment-details"
>
<div
class=
"enrollment-details"
>
...
...
lms/templates/shoppingcart/shopping_cart.html
View file @
2b8441a0
...
@@ -2,7 +2,6 @@
...
@@ -2,7 +2,6 @@
<
%
block
name=
"review_highlight"
>
class="active"
</
%
block>
<
%
block
name=
"review_highlight"
>
class="active"
</
%
block>
<
%!
<
%!
from
courseware
.
courses
import
get_course_about_section
from
django
.
core
.
urlresolvers
import
reverse
from
django
.
core
.
urlresolvers
import
reverse
from
edxmako
.
shortcuts
import
marketing_link
from
edxmako
.
shortcuts
import
marketing_link
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
utils
.
translation
import
ugettext
as
_
...
@@ -67,7 +66,7 @@ from openedx.core.lib.courses import course_image_url
...
@@ -67,7 +66,7 @@ from openedx.core.lib.courses import course_image_url
<div
class=
"clearfix"
>
<div
class=
"clearfix"
>
<div
class=
"image"
>
<div
class=
"image"
>
<img
class=
"item-image"
src=
"${course_image_url(course)}"
<img
class=
"item-image"
src=
"${course_image_url(course)}"
alt=
"${course.display_number_with_default | h} ${
get_course_about_section(request, course, 'title')
} ${_('Cover Image')}"
/>
alt=
"${course.display_number_with_default | h} ${
course.display_name_with_default
} ${_('Cover Image')}"
/>
</div>
</div>
<div
class=
"data-input"
>
<div
class=
"data-input"
>
## Translators: "Registration for:" is followed by a course name
## Translators: "Registration for:" is followed by a course name
...
...
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