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
1ab0825f
Commit
1ab0825f
authored
Apr 26, 2017
by
Andy Armstrong
Committed by
GitHub
Apr 26, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #14967 from edx/andya/empty-course-message
Add an empty course message to the course home page
parents
8fbf01cd
8c93eac6
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
166 additions
and
95 deletions
+166
-95
lms/static/sass/_build-lms-v2.scss
+1
-1
lms/static/sass/features/_course-experience.scss
+7
-0
openedx/features/course_experience/templates/course_experience/course-home-fragment.html
+1
-1
openedx/features/course_experience/templates/course_experience/course-outline-fragment.html
+131
-93
openedx/features/course_experience/tests/views/test_course_outline.py
+26
-0
No files found.
lms/static/sass/_build-lms-v2.scss
View file @
1ab0825f
...
...
@@ -25,4 +25,4 @@
// Features
@import
'features/bookmarks'
;
@import
'features/course-
outlin
e'
;
@import
'features/course-
experienc
e'
;
lms/static/sass/features/_course-
outlin
e.scss
→
lms/static/sass/features/_course-
experienc
e.scss
View file @
1ab0825f
// Course sidebar
.course-sidebar
{
@include
margin-left
(
0
);
@include
padding-left
(
$baseline
);
}
// Course outline
.course-outline
{
color
:
$lms-gray
;
...
...
openedx/features/course_experience/templates/course_experience/course-home-fragment.html
View file @
1ab0825f
...
...
@@ -64,7 +64,7 @@ from openedx.features.course_experience import UNIFIED_COURSE_EXPERIENCE_FLAG
<main
class=
"layout-col layout-col-b"
>
${HTML(outline_fragment.body_html())}
</main>
<aside
class=
"layout-col layout-col-a"
>
<aside
class=
"
course-sidebar
layout-col layout-col-a"
>
<div
class=
"section section-tools"
>
<h3
class=
"hd-6"
>
${_("Course Tools")}
</h3>
<ul
class=
"list-unstyled"
>
...
...
openedx/features/course_experience/templates/course_experience/course-outline-fragment.html
View file @
1ab0825f
...
...
@@ -5,7 +5,11 @@
<
%
namespace
name=
'static'
file=
'../static_content.html'
/>
<
%!
from
datetime
import
date
from
django
.
utils
.
translation
import
ugettext
as
_
from
openedx
.
core
.
djangolib
.
markup
import
HTML
,
Text
%
>
<
%
static:require_module_async
module_name=
"course_experience/js/course_outline_factory"
class_name=
"CourseOutlineFactory"
>
...
...
@@ -13,65 +17,77 @@ from django.utils.translation import ugettext as _
</
%
static:require
_module_async
>
<main
role=
"main"
class=
"course-outline"
id=
"main"
tabindex=
"-1"
>
<ol
class=
"block-tree"
role=
"tree"
>
% for section in blocks.get('children') or []:
<li
aria-expanded=
"true"
class=
"outline-item focusable section"
id=
"${ section['id'] }"
role=
"treeitem"
tabindex=
"0"
>
<div
class=
"section-name"
>
<h3>
${ section['display_name'] }
</h3>
</div>
<ol
class=
"outline-item focusable"
role=
"group"
tabindex=
"0"
>
% for subsection in section.get('children') or []:
<li
class=
"subsection ${ 'current' if subsection['current'] else '' }"
role=
"treeitem"
tabindex=
"-1"
aria-expanded=
"true"
>
<a
class=
"outline-item focusable"
href=
"${ subsection['lms_web_url'] }"
id=
"${ subsection['id'] }"
% if blocks.get('children'):
<ol
class=
"block-tree"
role=
"tree"
>
% for section in blocks.get('children'):
<li
aria-expanded=
"true"
class=
"outline-item focusable section"
id=
"${ section['id'] }"
role=
"treeitem"
tabindex=
"0"
>
<div
class=
"section-name"
>
<h3>
${ section['display_name'] }
</h3>
</div>
<ol
class=
"outline-item focusable"
role=
"group"
tabindex=
"0"
>
% for subsection in section.get('children') or []:
<li
class=
"subsection ${ 'current' if subsection['current'] else '' }"
role=
"treeitem"
tabindex=
"-1"
aria-expanded=
"true"
>
<div
class=
"subsection-text"
>
## Subsection title
<span
class=
"subsection-title"
>
${ subsection['display_name'] }
</span>
<a
class=
"outline-item focusable"
href=
"${ subsection['lms_web_url'] }"
id=
"${ subsection['id'] }"
>
<div
class=
"subsection-text"
>
## Subsection title
<span
class=
"subsection-title"
>
${ subsection['display_name'] }
</span>
<div
class=
"details"
>
## There are behavior differences between rendering of subsections which have
## exams (timed, graded, etc) and those that do not.
##
## Exam subsections expose exam status message field as well as a status icon
<
%
if
subsection
.
get
('
due
')
is
None:
#
examples:
Homework
,
Lab
,
etc
.
data_string =
subsection.get('format')
else:
if
'
special_exam_info
'
in
subsection:
data_string =
_('due
{
date
}')
<div
class=
"details"
>
## There are behavior differences between rendering of subsections which have
## exams (timed, graded, etc) and those that do not.
##
## Exam subsections expose exam status message field as well as a status icon
<
%
if
subsection
.
get
('
due
')
is
None:
#
examples:
Homework
,
Lab
,
etc
.
data_string =
subsection.get('format')
else:
data_string =
_("{subsection_format}
due
{{
date
}}").
format
(
subsection_format=
subsection.get('format'))
%
>
% if subsection.get('format') or 'special_exam_info' in subsection:
<span
class=
"subtitle"
>
% if 'special_exam' in subsection:
## Display the exam status icon and status message
<span
class=
"menu-icon icon fa ${subsection['special_exam_info'].get('suggested_icon', 'fa-pencil-square-o')} ${subsection['special_exam_info'].get('status', 'eligible')}"
aria-hidden=
"true"
></span>
<span
class=
"subtitle-name"
>
${subsection['special_exam_info'].get('short_description', '')}
</span>
if
'
special_exam_info
'
in
subsection:
data_string =
_('due
{
date
}')
else:
data_string =
_("{subsection_format}
due
{{
date
}}").
format
(
subsection_format=
subsection.get('format'))
%
>
% if subsection.get('format') or 'special_exam_info' in subsection:
<span
class=
"subtitle"
>
% if 'special_exam' in subsection:
## Display the exam status icon and status message
<span
class=
"menu-icon icon fa ${subsection['special_exam_info'].get('suggested_icon', 'fa-pencil-square-o')} ${subsection['special_exam_info'].get('status', 'eligible')}"
aria-hidden=
"true"
></span>
<span
class=
"subtitle-name"
>
${subsection['special_exam_info'].get('short_description', '')}
</span>
## completed exam statuses should not show the due date
## since the exam has already been submitted by the user
% if not subsection['special_exam_info'].get('in_completed_state', False):
## completed exam statuses should not show the due date
## since the exam has already been submitted by the user
% if not subsection['special_exam_info'].get('in_completed_state', False):
<span
class=
"localized-datetime subtitle-name"
data-datetime=
"${subsection.get('due')}"
data-string=
"${data_string}"
data-timezone=
"${user_timezone}"
data-language=
"${user_language}"
></span>
% endif
% else:
## non-graded section, we just show the exam format and the due date
## this is the standard case in edx-platform
<span
class=
"localized-datetime subtitle-name"
data-datetime=
"${subsection.get('due')}"
...
...
@@ -79,47 +95,69 @@ from django.utils.translation import ugettext as _
data-timezone=
"${user_timezone}"
data-language=
"${user_language}"
></span>
% endif
% else:
## non-graded section, we just show the exam format and the due date
## this is the standard case in edx-platform
<span
class=
"localized-datetime subtitle-name"
data-datetime=
"${subsection.get('due')}"
data-string=
"${data_string}"
data-timezone=
"${user_timezone}"
data-language=
"${user_language}"
></span>
% if 'graded' in subsection and subsection['graded']:
<span
class=
"menu-icon icon fa fa-pencil-square-o"
aria-hidden=
"true"
></span>
<span
class=
"sr"
>
${_("This content is graded")}
</span>
% if 'graded' in subsection and subsection['graded']:
<span
class=
"menu-icon icon fa fa-pencil-square-o"
aria-hidden=
"true"
></span>
<span
class=
"sr"
>
${_("This content is graded")}
</span>
% endif
% endif
</span>
% endif
</span>
% endif
</div>
<!-- /details -->
</div>
<!-- /subsection-text -->
<div
class=
"subsection-actions"
>
## Resume button (if last visited section)
% if subsection['current']:
<span
class=
"sr-only"
>
${ _("This is your last visited course section.") }
</span>
<span
class=
"resume-right"
>
<b>
${ _("Resume Course") }
</b>
<span
class=
"icon fa fa-arrow-circle-right"
aria-hidden=
"true"
></span>
</span>
%endif
</div>
</a>
</li>
% endfor
</ol>
</li>
% endfor
</ol>
</div>
<!-- /details -->
</div>
<!-- /subsection-text -->
<div
class=
"subsection-actions"
>
## Resume button (if last visited section)
% if subsection['current']:
<span
class=
"sr-only"
>
${ _("This is your last visited course section.") }
</span>
<span
class=
"resume-right"
>
<b>
${ _("Resume Course") }
</b>
<span
class=
"icon fa fa-arrow-circle-right"
aria-hidden=
"true"
></span>
</span>
%endif
</div>
</a>
</li>
% endfor
</ol>
</li>
% endfor
</ol>
% else:
<div
class=
"well depth-0 message-area"
>
<div
class=
"copy-large"
>
<span
class=
"icon fa fa-calendar-o"
aria-hidden=
"true"
></span>
<
%
course_started =
course.start.date()
<=
date
.
today
()
%
>
% if course.start_date_is_still_default:
${_("This course has not started yet.")}
% elif course_started:
${_("We're still working on course content.")}
% else:
${Text(_("This course has not started yet, and will launch on {launch_date_html}.")).format(
launch_date_html=HTML(
'
<span
'
'
class=
"localized-datetime start-date"
'
'
data-datetime=
"{start_date}"
'
'
data-format=
"shortDate"
'
'
data-timezone=
"{user_timezone}"
'
'
data-language=
"{user_language}"
'
'
></span>
'
).format(
start_date=course.start,
user_timezone=user_timezone,
user_language=user_language,
),
)}
% endif
</div>
</div>
% endif
</main>
<
%
static:require_module_async
module_name=
"js/dateutil_factory"
class_name=
"DateUtilFactory"
>
...
...
openedx/features/course_experience/tests/views/test_course_outline.py
View file @
1ab0825f
...
...
@@ -2,6 +2,7 @@
Tests for the Course Outline view and supporting views.
"""
import
datetime
import
ddt
from
mock
import
patch
import
json
...
...
@@ -12,10 +13,13 @@ from student.models import CourseEnrollment
from
student.tests.factories
import
UserFactory
from
xmodule.modulestore.tests.django_utils
import
SharedModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
,
ItemFactory
from
xmodule.course_module
import
DEFAULT_START_DATE
from
.test_course_home
import
course_home_url
TEST_PASSWORD
=
'test'
FUTURE_DAY
=
datetime
.
datetime
.
now
()
+
datetime
.
timedelta
(
days
=
30
)
PAST_DAY
=
datetime
.
datetime
.
now
()
-
datetime
.
timedelta
(
days
=
30
)
class
TestCourseOutlinePage
(
SharedModuleStoreTestCase
):
...
...
@@ -160,3 +164,25 @@ class TestCourseOutlinePreview(SharedModuleStoreTestCase):
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertNotContains
(
response
,
'Future Chapter'
)
@ddt.ddt
class
TestEmptyCourseOutlinePage
(
SharedModuleStoreTestCase
):
"""
Test the new course outline view.
"""
@ddt.data
(
(
FUTURE_DAY
,
'This course has not started yet, and will launch on'
),
(
PAST_DAY
,
"We're still working on course content."
),
(
DEFAULT_START_DATE
,
'This course has not started yet.'
),
)
@ddt.unpack
def
test_empty_course_rendering
(
self
,
start_date
,
expected_text
):
course
=
CourseFactory
.
create
(
start
=
start_date
)
test_user
=
UserFactory
(
password
=
TEST_PASSWORD
)
CourseEnrollment
.
enroll
(
test_user
,
course
.
id
)
self
.
client
.
login
(
username
=
test_user
.
username
,
password
=
TEST_PASSWORD
)
url
=
course_home_url
(
course
)
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertContains
(
response
,
expected_text
)
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