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):
throttle
=
EnrollmentUserThrottle
()
self
.
rate_limit
,
rate_duration
=
throttle
.
parse_rate
(
throttle
.
rate
)
self
.
course
=
CourseFactory
.
create
()
# Load a CourseOverview. This initial load should result in a cache
# miss; the modulestore is queried and course metadata is cached.
__
=
CourseOverview
.
get_from_id
(
self
.
course
.
id
)
# Pass emit_signals when creating the course so it would be cached
# as a CourseOverview.
self
.
course
=
CourseFactory
.
create
(
emit_signals
=
True
)
self
.
user
=
UserFactory
.
create
(
username
=
self
.
USERNAME
,
...
...
@@ -336,7 +335,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
requesting user.
"""
# 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
:
CourseModeFactory
.
create
(
course_id
=
unicode
(
course
.
id
),
...
...
@@ -345,7 +344,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
)
self
.
assert_enrollment_status
(
course_id
=
unicode
(
course
.
id
),
max_mongo_calls
=
1
,
max_mongo_calls
=
0
,
)
# Verify the user himself can see both of his enrollments.
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
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
microsite_configuration
import
microsite
from
django.contrib.staticfiles.storage
import
staticfiles_storage
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
def
get_visible_courses
():
"""
Return the set of CourseDescriptors that should be visible in this branded instance
"""
filtered_by_org
=
microsite
.
get_value
(
'course_org_filter'
)
_courses
=
modulestore
()
.
get_courses
(
org
=
filtered_by_org
)
courses
=
[
c
for
c
in
_courses
if
isinstance
(
c
,
CourseDescriptor
)]
courses
=
CourseOverview
.
get_all_courses
(
org
=
filtered_by_org
)
courses
=
sorted
(
courses
,
key
=
lambda
course
:
course
.
number
)
subdomain
=
microsite
.
get_value
(
'subdomain'
,
'default'
)
# See if we have filtered course listings in this domain
filtered_visible_ids
=
None
# 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
:
filtered_visible_ids
=
frozenset
(
[
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
from
django.http
import
HttpResponse
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
class_dashboard
import
dashboard_data
...
...
@@ -21,7 +21,7 @@ def has_instructor_access_for_class(user, course_id):
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
))
...
...
lms/djangoapps/course_wiki/middleware.py
View file @
2b8441a0
...
...
@@ -6,7 +6,7 @@ from django.shortcuts import redirect
from
django.core.exceptions
import
PermissionDenied
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
student.models
import
CourseEnrollment
from
util.request
import
course_id_from_url
...
...
@@ -29,7 +29,7 @@ class WikiAccessMiddleware(object):
if
course_id
:
# See if we are able to view the course. If we are, redirect to it
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
))
except
Http404
:
# 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):
# delegate the work to type-specific functions.
# (start with more specific types, then get more general)
if
isinstance
(
obj
,
CourseDescriptor
):
return
_has_access_course
_desc
(
user
,
action
,
obj
)
return
_has_access_course
(
user
,
action
,
obj
)
if
isinstance
(
obj
,
CourseOverview
):
return
_has_access_course
_overview
(
user
,
action
,
obj
)
return
_has_access_course
(
user
,
action
,
obj
)
if
isinstance
(
obj
,
ErrorDescriptor
):
return
_has_access_error_desc
(
user
,
action
,
obj
,
course_key
)
...
...
@@ -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.
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
checking access.
...
...
@@ -270,7 +270,7 @@ def _can_enroll_courselike(user, courselike):
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.
...
...
@@ -297,11 +297,21 @@ def _has_access_courselike(user, action, courselike):
NOTE: this is not checking whether user is actually enrolled in the course.
"""
# delegate to generic descriptor check to check start dates
return
_has_access_descriptor
(
user
,
'load'
,
course
,
course
.
id
)
response
=
(
_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
():
return
_can_enroll_courselike
(
user
,
course
)
"""
Returns whether the user can enroll in the course.
"""
return
_can_enroll_courselike
(
user
,
courselike
)
def
see_exists
():
"""
...
...
@@ -317,8 +327,8 @@ def _has_access_courselike(user, action, courselike):
but also allow course staff to see this.
"""
return
(
_has_catalog_visibility
(
course
,
CATALOG_VISIBILITY_CATALOG_AND_ABOUT
)
or
_has_staff_access_to_descriptor
(
user
,
course
,
cours
e
.
id
)
_has_catalog_visibility
(
course
like
,
CATALOG_VISIBILITY_CATALOG_AND_ABOUT
)
or
_has_staff_access_to_descriptor
(
user
,
course
like
,
courselik
e
.
id
)
)
def
can_see_about_page
():
...
...
@@ -328,75 +338,25 @@ def _has_access_courselike(user, action, courselike):
but also allow course staff to see this.
"""
return
(
_has_catalog_visibility
(
course
,
CATALOG_VISIBILITY_CATALOG_AND_ABOUT
)
or
_has_catalog_visibility
(
course
,
CATALOG_VISIBILITY_ABOUT
)
or
_has_staff_access_to_descriptor
(
user
,
course
,
cours
e
.
id
)
_has_catalog_visibility
(
course
like
,
CATALOG_VISIBILITY_CATALOG_AND_ABOUT
)
or
_has_catalog_visibility
(
course
like
,
CATALOG_VISIBILITY_ABOUT
)
or
_has_staff_access_to_descriptor
(
user
,
course
like
,
courselik
e
.
id
)
)
checkers
=
{
'load'
:
can_load
,
'view_courseware_with_prerequisites'
:
lambda
:
_can_view_courseware_with_prerequisites
(
user
,
course
),
'load_mobile'
:
lambda
:
can_load
()
and
_can_load_course_on_mobile
(
user
,
course
),
lambda
:
_can_view_courseware_with_prerequisites
(
user
,
course
like
),
'load_mobile'
:
lambda
:
can_load
()
and
_can_load_course_on_mobile
(
user
,
course
like
),
'enroll'
:
can_enroll
,
'see_exists'
:
see_exists
,
'staff'
:
lambda
:
_has_staff_access_to_descriptor
(
user
,
course
,
cours
e
.
id
),
'instructor'
:
lambda
:
_has_instructor_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
like
,
courselik
e
.
id
),
'see_in_catalog'
:
can_see_in_catalog
,
'see_about_page'
:
can_see_about_page
,
}
return
_dispatch
(
checkers
,
action
,
user
,
course
)
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
))
return
_dispatch
(
checkers
,
action
,
user
,
courselike
)
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
from
edxmako.shortcuts
import
render_to_string
from
xmodule.modulestore
import
ModuleStoreEnum
from
opaque_keys.edx.keys
import
CourseKey
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
static_replace
import
replace_static_urls
...
...
@@ -37,6 +36,7 @@ from student.models import CourseEnrollment
import
branding
from
opaque_keys.edx.keys
import
UsageKey
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -58,7 +58,6 @@ def get_course(course_id, depth=0):
return
course
# TODO please rename this function to get_course_by_key at next opportunity!
def
get_course_by_id
(
course_key
,
depth
=
0
):
"""
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=
check_if_enrolled: If true, additionally verifies that the user is either enrolled in the course
or has staff access.
"""
assert
isinstance
(
course_key
,
CourseKey
)
course
=
get_course_by_id
(
course_key
,
depth
=
depth
)
access_response
=
has_access
(
user
,
action
,
course
,
course_key
)
course
=
get_course_by_id
(
course_key
,
depth
)
check_course_access
(
course
,
user
,
action
,
check_if_enrolled
)
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
:
# 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=
raise
CoursewareAccessException
(
access_response
)
if
check_if_enrolled
:
# Verify that the user is either enrolled in the course or a staff member.
# If user is not enrolled, raise UserNotEnrolled exception that will be caught by middleware.
if
not
((
user
.
id
and
CourseEnrollment
.
is_enrolled
(
user
,
course_key
))
or
has_access
(
user
,
'staff'
,
course
)):
raise
UserNotEnrolled
(
course_key
)
return
course
# Verify that the user is either enrolled in the course or a staff
# member. If user is not enrolled, raise UserNotEnrolled exception
# that will be caught by middleware.
if
not
((
user
.
id
and
CourseEnrollment
.
is_enrolled
(
user
,
course
.
id
))
or
has_access
(
user
,
'staff'
,
course
)):
raise
UserNotEnrolled
(
course
.
id
)
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
))
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
):
"""
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):
Valid keys:
- overview
- title
- university
- number
- short_description
- description
- key_dates (includes start, end, exams, etc)
...
...
@@ -159,6 +174,7 @@ def get_course_about_section(request, course, section_key):
- syllabus
- textbook
- faq
- effort
- more_info
- ocw_links
"""
...
...
@@ -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
# good format for defining so many snippets of text/html.
# TODO: Remove number, instructors from this set
html_sections
=
{
'short_description'
,
'description'
,
...
...
@@ -180,8 +195,6 @@ def get_course_about_section(request, course, section_key):
'textbook'
,
'faq'
,
'more_info'
,
'number'
,
'instructors'
,
'overview'
,
'effort'
,
'end_date'
,
...
...
@@ -225,12 +238,6 @@ def get_course_about_section(request, course, section_key):
section_key
,
course
.
location
.
to_deprecated_string
()
)
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
))
...
...
@@ -366,22 +373,6 @@ def get_course_syllabus_section(course, 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
):
'''
Returns a list of courses available, sorted by course.number
...
...
@@ -400,6 +391,16 @@ def get_courses(user, domain=None):
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
):
"""
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
from
django.test
import
TestCase
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
student.models
import
Registration
...
...
@@ -151,30 +151,27 @@ class CourseAccessTestMixin(TestCase):
"""
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
a CourseOverview of the given
course.
Test with both the given course and
with a CourseOverview of the given
course.
Arguments:
user (User): a user.
action (str): type of access to test.
See access.py:COURSE_OVERVIEW_SUPPORTED_ACTIONS.
course (CourseDescriptor): a 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
):
"""
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
a CourseOverview of the given
course.
Test with both the given course and
with a CourseOverview of the given
course.
Arguments:
user (User): a user.
action (str): type of access to test.
See access.py:COURSE_OVERVIEW_SUPPORTED_ACTIONS.
course (CourseDescriptor): a course.
Note:
...
...
@@ -184,5 +181,4 @@ class CourseAccessTestMixin(TestCase):
stack traces of failed tests easier to understand at a glance.
"""
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):
mock_unit
.
start
=
start
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
)
tomorrow
=
datetime
.
datetime
.
now
(
pytz
.
utc
)
+
datetime
.
timedelta
(
days
=
1
)
...
...
@@ -248,11 +248,11 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
enrollment_domain
=
''
)
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
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
# and not specifically allowed
...
...
@@ -262,7 +262,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
invitation_only
=
True
)
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
course
=
Mock
(
...
...
@@ -270,7 +270,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
enrollment_domain
=
''
,
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
course
=
Mock
(
...
...
@@ -278,7 +278,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
enrollment_domain
=
''
,
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
):
"""Ensure has_access handles a user being passed as null"""
...
...
@@ -296,40 +296,30 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
id
=
course_id
,
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
_desc
(
user
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
staff
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
staff
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
user
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
user
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'see_about_page'
,
course
))
# Now set visibility to just about page
course
=
Mock
(
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
catalog_visibility
=
CATALOG_VISIBILITY_ABOUT
)
self
.
assertFalse
(
access
.
_has_access_course
_desc
(
user
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
user
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
staff
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
_desc
(
staff
,
'see_about_page'
,
course
))
self
.
assertFalse
(
access
.
_has_access_course
(
user
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
user
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'see_in_catalog'
,
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
course
=
Mock
(
id
=
SlashSeparatedCourseKey
(
'edX'
,
'test'
,
'2012_Fall'
),
catalog_visibility
=
CATALOG_VISIBILITY_NONE
)
self
.
assertFalse
(
access
.
_has_access_course_desc
(
user
,
'see_in_catalog'
,
course
))
self
.
assertFalse
(
access
.
_has_access_course_desc
(
user
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course_desc
(
staff
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course_desc
(
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
)
self
.
assertFalse
(
access
.
_has_access_course
(
user
,
'see_in_catalog'
,
course
))
self
.
assertFalse
(
access
.
_has_access_course
(
user
,
'see_about_page'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'see_in_catalog'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'see_about_page'
,
course
))
@patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENABLE_PREREQUISITE_COURSES'
:
True
,
'MILESTONES_APP'
:
True
})
def
test_access_on_course_with_pre_requisites
(
self
):
...
...
@@ -351,16 +341,16 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
# user should not be able to load course even if enrolled
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
.
assertIsInstance
(
response
,
access_response
.
MilestoneError
)
# Staff can always access course
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
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
(
(
True
,
True
,
True
),
...
...
@@ -377,10 +367,10 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
descriptor
.
mobile_available
=
mobile_available
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
)
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
})
def
test_courseware_page_unfulfilled_prereqs
(
self
):
...
...
@@ -552,7 +542,6 @@ class CourseOverviewAccessTestCase(ModuleStoreTestCase):
user_attr_name (str): the name of the attribute on self that is the
User 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
the CourseDescriptor to test with.
"""
...
...
lms/djangoapps/courseware/tests/test_courses.py
View file @
2b8441a0
...
...
@@ -18,7 +18,7 @@ from courseware.courses import (
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.tests.helpers
import
get_request_for_user
from
courseware.model_data
import
FieldDataCache
...
...
@@ -30,7 +30,7 @@ from xmodule.modulestore import ModuleStoreEnum
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
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
XModuleXmlImportTest
...
...
@@ -40,6 +40,7 @@ TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
@attr
(
'shard_1'
)
@ddt.ddt
class
CoursesTest
(
ModuleStoreTestCase
):
"""Test methods related to fetching courses."""
...
...
@@ -57,16 +58,28 @@ class CoursesTest(ModuleStoreTestCase):
cms_url
=
u"//{}/course/{}"
.
format
(
CMS_BASE_TEST
,
unicode
(
self
.
course
.
location
))
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
()
course
=
CourseFactory
.
create
(
visible_to_staff_only
=
True
)
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
.
access_response
.
error_code
,
"not_visible_to_user"
)
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'
)
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
from
courseware.access_response
import
StartDateError
from
courseware.access_utils
import
in_preview_mode
from
courseware.courses
import
(
get_courses
,
get_course
,
get_course_by_id
,
get_studio_url
,
get_course_with_access
,
get_courses
,
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_start_date
,
UserNotEnrolled
)
UserNotEnrolled
)
from
courseware.masquerade
import
setup_masquerade
from
openedx.core.djangoapps.credit.api
import
(
get_credit_requirement_status
,
...
...
@@ -802,11 +808,8 @@ def course_about(request, course_id):
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
with
modulestore
()
.
bulk_operations
(
course_key
):
permission_name
=
microsite
.
get_value
(
'COURSE_ABOUT_VISIBILITY_PERMISSION'
,
settings
.
COURSE_ABOUT_VISIBILITY_PERMISSION
)
course
=
get_course_with_access
(
request
.
user
,
permission_name
,
course_key
)
permission
=
get_permission_for_course_about
()
course
=
get_course_with_access
(
request
.
user
,
permission
,
course_key
)
if
microsite
.
get_value
(
'ENABLE_MKTG_SITE'
,
settings
.
FEATURES
.
get
(
'ENABLE_MKTG_SITE'
,
False
)):
return
redirect
(
reverse
(
'info'
,
args
=
[
course
.
id
.
to_deprecated_string
()]))
...
...
@@ -1066,7 +1069,7 @@ def submission_history(request, course_id, student_username, location):
except
(
InvalidKeyError
,
AssertionError
):
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
))
# 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
from
courseware.access
import
has_access
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
from
django_comment_common.signals
import
(
thread_created
,
...
...
@@ -770,7 +770,7 @@ def users(request, course_id):
course_key
=
CourseKey
.
from_string
(
course_id
)
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
:
# course didn't exist, or requesting user does not have access to it.
return
JsonError
(
status
=
404
)
...
...
lms/templates/course.html
View file @
2b8441a0
...
...
@@ -2,30 +2,28 @@
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
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"
/>
<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()])}"
>
<header
class=
"course-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>
</header>
<div
class=
"course-info"
aria-hidden=
"true"
>
<h2
class=
"course-name"
>
<span
class=
"course-organization"
>
${
get_course_about_section(request, course, 'university')
}
</span>
<span
class=
"course-code"
>
${course.display_number_with_default}
</span>
<span
class=
"course-title"
>
${
get_course_about_section(request, course, 'title')
}
</span>
<span
class=
"course-organization"
>
${
course.display_org_with_default | h
}
</span>
<span
class=
"course-code"
>
${course.display_number_with_default
| h
}
</span>
<span
class=
"course-title"
>
${
course.display_name_with_default
}
</span>
</h2>
<div
class=
"course-date"
aria-hidden=
"true"
>
${_("Starts")}: ${course.start_datetime_text()}
</div>
</div>
<div
class=
"sr"
>
<ul>
<li>
${
get_course_about_section(request, course, 'university')
}
</li>
<li>
${course.display_number_with_default}
</li>
<li>
${
course.display_org_with_default | h
}
</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>
</ul>
</div>
...
...
lms/templates/courseware/course_about.html
View file @
2b8441a0
...
...
@@ -13,7 +13,7 @@ from openedx.core.lib.courses import course_image_url
<
%
block
name=
"headextra"
>
## 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)
<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')}"
/>
</
%
block>
...
...
@@ -102,7 +102,7 @@ from openedx.core.lib.courses import course_image_url
<script
src=
"${static.url('js/course_info.js')}"
></script>
</
%
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"
>
<header
class=
"course-profile"
>
...
...
@@ -111,9 +111,9 @@ from openedx.core.lib.courses import course_image_url
<section
class=
"intro"
>
<hgroup>
<h1>
${
get_course_about_section(request, course, "title")
}
${
course.display_name_with_default
}
% 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
</h1>
</hgroup>
...
...
@@ -220,10 +220,10 @@ from openedx.core.lib.courses import course_image_url
## or something allowing themes to do whatever they
## want here (and on this whole page, really).
% 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>
</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>
</a>
% else:
...
...
@@ -235,7 +235,7 @@ from openedx.core.lib.courses import course_image_url
##
Twitter
account
.
{
url
}
should
appear
at
the
end
of
the
text
.
tweet_text =
_("I
just
enrolled
in
{
number
}
{
title
}
through
{
account
}
:
{
url
}").
format
(
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
),
url=
u"http://{domain}{path}".format(
domain=
site_domain,
...
...
@@ -250,7 +250,7 @@ from openedx.core.lib.courses import course_image_url
subject=
_("Take
a
course
with
{
platform
}
online
").
format
(
platform=
platform_name),
body=
_("I
just
enrolled
in
{
number
}
{
title
}
through
{
platform
}
{
url
}").
format
(
number=
course.number,
title=
get_course_about_section(request,
course
,
'
title
')
,
title=
course.display_name_with_default
,
platform=
platform_name,
url=
u"http://{domain}{path}".format(
domain=
site_domain,
...
...
lms/templates/dashboard/_dashboard_course_listing.html
View file @
2b8441a0
...
...
@@ -7,7 +7,6 @@ from django.utils.translation import ugettext as _
from
django
.
utils
.
translation
import
ungettext
from
django
.
core
.
urlresolvers
import
reverse
from
markupsafe
import
escape
from
courseware
.
courses
import
get_course_university_about_section
from
course_modes
.
models
import
CourseMode
from
course_modes
.
helpers
import
enrollment_mode_display
from
student
.
helpers
import
(
...
...
@@ -99,7 +98,7 @@ from student.helpers import (
% endif
</h3>
<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-date-block"
data-tooltip=
"Hi"
>
% if course_overview.has_ended():
...
...
lms/templates/shoppingcart/receipt.html
View file @
2b8441a0
...
...
@@ -3,7 +3,6 @@
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
utils
.
translation
import
ungettext
from
django
.
core
.
urlresolvers
import
reverse
from
courseware
.
courses
import
get_course_about_section
,
get_course_by_id
from
markupsafe
import
escape
from
microsite_configuration
import
microsite
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=
"image"
>
<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
class=
"data-input"
>
...
...
lms/templates/shoppingcart/registration_code_receipt.html
View file @
2b8441a0
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
core
.
urlresolvers
import
reverse
from
courseware
.
courses
import
get_course_about_section
from
openedx
.
core
.
lib
.
courses
import
course_image_url
%
>
<
%
inherit
file=
"../main.html"
/>
...
...
@@ -21,7 +20,7 @@ from openedx.core.lib.courses import course_image_url
<img
class=
"item-image"
src=
"${course_image_url(course)}"
alt=
"${_("
{
course_number
}
{
course_title
}
Cover
Image
").
format
(
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
class=
"enrollment-details"
>
...
...
lms/templates/shoppingcart/registration_code_redemption.html
View file @
2b8441a0
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
from
django
.
core
.
urlresolvers
import
reverse
from
courseware
.
courses
import
get_course_about_section
from
openedx
.
core
.
lib
.
courses
import
course_image_url
%
>
<
%
inherit
file=
"../main.html"
/>
...
...
@@ -21,7 +20,7 @@ from openedx.core.lib.courses import course_image_url
<img
class=
"item-image"
src=
"${course_image_url(course)}"
alt=
"${_("
{
course_number
}
{
course_title
}
Cover
Image
").
format
(
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
class=
"enrollment-details"
>
...
...
lms/templates/shoppingcart/shopping_cart.html
View file @
2b8441a0
...
...
@@ -2,7 +2,6 @@
<
%
block
name=
"review_highlight"
>
class="active"
</
%
block>
<
%!
from
courseware
.
courses
import
get_course_about_section
from
django
.
core
.
urlresolvers
import
reverse
from
edxmako
.
shortcuts
import
marketing_link
from
django
.
utils
.
translation
import
ugettext
as
_
...
...
@@ -67,7 +66,7 @@ from openedx.core.lib.courses import course_image_url
<div
class=
"clearfix"
>
<div
class=
"image"
>
<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
class=
"data-input"
>
## 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