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
dd8e879e
Unverified
Commit
dd8e879e
authored
Dec 18, 2017
by
Matthew Piatetsky
Committed by
GitHub
Dec 18, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #16892 from edx/LEARNER-3072
LEARNER-3072 Add bundle purchase button to course dashboard
parents
e705ff09
000bbaf0
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
76 additions
and
395 deletions
+76
-395
common/djangoapps/student/views.py
+27
-2
lms/static/sass/_experiments.scss
+24
-382
lms/templates/dashboard/_dashboard_course_listing.html
+25
-11
No files found.
common/djangoapps/student/views.py
View file @
dd8e879e
...
@@ -83,10 +83,14 @@ from openedx.core.djangoapps.external_auth.login_and_register import register as
...
@@ -83,10 +83,14 @@ from openedx.core.djangoapps.external_auth.login_and_register import register as
from
openedx.core.djangoapps.external_auth.models
import
ExternalAuthMap
from
openedx.core.djangoapps.external_auth.models
import
ExternalAuthMap
from
openedx.core.djangoapps.lang_pref
import
LANGUAGE_KEY
from
openedx.core.djangoapps.lang_pref
import
LANGUAGE_KEY
from
openedx.core.djangoapps.programs.models
import
ProgramsApiConfig
from
openedx.core.djangoapps.programs.models
import
ProgramsApiConfig
from
openedx.core.djangoapps.programs.utils
import
ProgramProgressMeter
from
openedx.core.djangoapps.programs.utils
import
(
ProgramDataExtender
,
ProgramProgressMeter
)
from
openedx.core.djangoapps.site_configuration
import
helpers
as
configuration_helpers
from
openedx.core.djangoapps.site_configuration
import
helpers
as
configuration_helpers
from
openedx.core.djangoapps.theming
import
helpers
as
theming_helpers
from
openedx.core.djangoapps.theming
import
helpers
as
theming_helpers
from
openedx.core.djangoapps.user_api.preferences
import
api
as
preferences_api
from
openedx.core.djangoapps.user_api.preferences
import
api
as
preferences_api
from
openedx.core.djangoapps.waffle_utils
import
WaffleFlagNamespace
,
WaffleFlag
from
openedx.core.djangolib.markup
import
HTML
from
openedx.core.djangolib.markup
import
HTML
from
openedx.features.course_experience
import
course_home_url_name
from
openedx.features.course_experience
import
course_home_url_name
from
openedx.features.enterprise_support.api
import
get_dashboard_consent_notification
from
openedx.features.enterprise_support.api
import
get_dashboard_consent_notification
...
@@ -780,8 +784,28 @@ def dashboard(request):
...
@@ -780,8 +784,28 @@ def dashboard(request):
# is passed in the template context to allow rendering of program-related
# is passed in the template context to allow rendering of program-related
# information on the dashboard.
# information on the dashboard.
meter
=
ProgramProgressMeter
(
request
.
site
,
user
,
enrollments
=
course_enrollments
)
meter
=
ProgramProgressMeter
(
request
.
site
,
user
,
enrollments
=
course_enrollments
)
ecommerce_service
=
EcommerceService
()
inverted_programs
=
meter
.
invert_programs
()
inverted_programs
=
meter
.
invert_programs
()
urls
,
program_data
=
{},
{}
bundles_on_dashboard_flag
=
WaffleFlag
(
WaffleFlagNamespace
(
name
=
u'student.experiments'
),
u'bundles_on_dashboard'
)
if
(
bundles_on_dashboard_flag
.
is_enabled
()):
programs_data
=
meter
.
programs
if
programs_data
:
program_data
=
meter
.
programs
[
0
]
program_data
=
ProgramDataExtender
(
program_data
,
request
.
user
)
.
extend
()
course_data
=
meter
.
progress
(
programs
=
[
program_data
],
count_only
=
False
)[
0
]
program_data
.
pop
(
'courses'
)
skus
=
program_data
.
get
(
'skus'
)
urls
=
{
'commerce_api_url'
:
reverse
(
'commerce_api:v0:baskets:create'
),
'buy_button_url'
:
ecommerce_service
.
get_checkout_page_url
(
*
skus
)
}
urls
[
'completeProgramURL'
]
=
urls
[
'buy_button_url'
]
+
'&bundle='
+
program_data
.
get
(
'uuid'
)
# Construct a dictionary of course mode information
# Construct a dictionary of course mode information
# used to render the course list. We re-use the course modes dict
# used to render the course list. We re-use the course modes dict
# we loaded earlier to avoid hitting the database.
# we loaded earlier to avoid hitting the database.
...
@@ -879,6 +903,8 @@ def dashboard(request):
...
@@ -879,6 +903,8 @@ def dashboard(request):
course_enrollments
=
[
enr
for
enr
in
course_enrollments
if
entitlement
.
enrollment_course_run
.
course_id
!=
enr
.
course_id
]
# pylint: disable=line-too-long
course_enrollments
=
[
enr
for
enr
in
course_enrollments
if
entitlement
.
enrollment_course_run
.
course_id
!=
enr
.
course_id
]
# pylint: disable=line-too-long
context
=
{
context
=
{
'urls'
:
urls
,
'program_data'
:
program_data
,
'enterprise_message'
:
enterprise_message
,
'enterprise_message'
:
enterprise_message
,
'consent_required_courses'
:
consent_required_courses
,
'consent_required_courses'
:
consent_required_courses
,
'enterprise_customer_name'
:
enterprise_customer_name
,
'enterprise_customer_name'
:
enterprise_customer_name
,
...
@@ -921,7 +947,6 @@ def dashboard(request):
...
@@ -921,7 +947,6 @@ def dashboard(request):
'display_sidebar_on_dashboard'
:
display_sidebar_on_dashboard
,
'display_sidebar_on_dashboard'
:
display_sidebar_on_dashboard
,
}
}
ecommerce_service
=
EcommerceService
()
if
ecommerce_service
.
is_enabled
(
request
.
user
):
if
ecommerce_service
.
is_enabled
(
request
.
user
):
context
.
update
({
context
.
update
({
'use_ecommerce_payment_flow'
:
True
,
'use_ecommerce_payment_flow'
:
True
,
...
...
lms/static/sass/_experiments.scss
View file @
dd8e879e
...
@@ -4,396 +4,38 @@
...
@@ -4,396 +4,38 @@
// Please list the ticket number of the experiment
// Please list the ticket number of the experiment
// --------------------
// --------------------
// LEARNER-
1726 Track Selection V3
// LEARNER-
3072 Program Purchase on dashboard
/* This css was added as part of the LEARNER-1726 experiment */
/* This css was added as part of the LEARNER-3072 experiment */
.v2.register-choice
{
margin
:
0
2%
20px
0
!
important
}
.v2.register-choice-certificate
.list-actions
{
text-align
:
left
!
important
;
}
.v2.register-choice-continue
.list-actions
{
margin-bottom
:
0
!
important
;
}
.v2.register-choice-continue
.action-select
{
display
:
inline-block
!
important
;
list-style-type
:
none
!
important
;
width
:
100%
!
important
;
}
.v2.register-choice-continue
.continue-link
{
display
:
inline-block
!
important
;
padding
:
10px
15px
!
important
;
border-radius
:
3px
!
important
;
border
:
1px
solid
#d7548e
!
important
;
box-shadow
:
0
2px
1px
0
#982c62
!
important
;
background
:
white
!
important
;
text-align
:
center
!
important
;
color
:
#d7548e
!
important
;
float
:
left
!
important
;
font-size
:
15px
;
font-weight
:
500
!
important
;
}
.v2.register-choice-v2-donate
{
height
:
300px
;
background
:
none
!
important
;
border-top-color
:
grey
!
important
;
border-top-width
:
1px
!
important
;
}
@media
screen
and
(
min-width
:
375px
)
{
.v2.register-choice-v2-donate
{
height
:
250px
;
}
}
.v2.register-choice-v2-donate
.list-actions
{
margin-bottom
:
0
!
important
;
}
.v2.register-choice-v2-donate
.list-actions
a
{
background
:
transparent
!
important
;
color
:
#0075b4
!
important
;
box-shadow
:
none
!
important
;
text-decoration
:
underline
!
important
;
border
:
none
!
important
;
white-space
:
normal
;
}
.v2.register-choice-v2-donate
.wrapper-copy-inline
{
height
:
70px
!
important
;
width
:
100%
!
important
;
display
:
flex
!
important
;
}
.v2.register-choice-v2-donate
.wrapper-copy
{
width
:
70%
!
important
;
height
:
auto
!
important
;
}
.v2.page-header
{
padding
:
0
;
}
.v2
img
{
margin-top
:
20px
;
margin-left
:
5px
;
}
.v2
.continue-link
{
font-weight
:
bold
!
important
;
}
.v2.register-choice-certificate
,
.v2.register-choice-continue
,
.v2.register-choice-view
{
width
:
100%
;
}
.v2.register-choice-continue
{
border-color
:
#d7548e
!
important
;
}
.v2
.wrapper-copy-inline
{
max-height
:
115px
;
}
.v2.register-choice-v2-donate
.wrapper-copy-inline
{
display
:
block
!
important
;
}
.v2.register-choice-v2-donate
.copy-inline
{
width
:
100%
!
important
;
}
.v2.register-choice-v2-donate
.list-actions
{
.complete-program-dashboard-button
{
width
:
100%
!
important
;
float
:
right
;
margin-top
:
20px
!
important
;
display
:
block
;
text-align
:
center
!
important
;
box-sizing
:
border-box
;
}
color
:
rgb
(
242
,
248
,
251
)
!
important
;
.v2
.wrapper-copy-inline
.wrapper-copy
{
width
:
100%
!
important
;
}
.v2
input
,
.v2
a
{
font-size
:
15px
!
important
;
}
.v2
button
{
background-color
:
rgb
(
0
,
103
,
0
);
border-color
:
rgb
(
0
,
103
,
0
);
border-radius
:
2px
;
box-shadow
:
rgb
(
0
,
77
,
0
)
0
2px
1px
0
;
cursor
:
pointer
;
cursor
:
pointer
;
font-family
:
$font-family-sans-serif
;
background
:
rgb
(
0
,
129
,
0
)
none
repeat
scroll
0%
0%
/
auto
padding-box
border-box
!
important
;
height
:
auto
;
font
:
normal
normal
600
normal
15px
/
normal
"Open Sans"
,
"Helvetica Neue"
,
Helvetica
,
Arial
,
sans-serif
;
margin-right
:
4px
;
padding
:
7px
;
margin-top
:
0
;
transition
:
color
0
.25s
ease-in-out
0s
,
background
0
.25s
ease-in-out
0s
,
box-shadow
0
.25s
ease-in-out
0s
;
padding
:
10px
15px
;
text-decoration
:
none
!
important
;
width
:
initial
;
border
:
none
!
important
;
background-image
:
none
!
important
;
margin-top
:
5px
;
font-size
:
14px
!
important
;
font-weight
:
500
!
important
;
&
:hover
,
&
:focus
{
background-color
:
#009b00
!
important
;
border-color
:
#009b00
;
box-shadow
:
#004d00
0
2px
1px
0
;
}
}
.savings-message
{
margin-top
:
10px
;
font-size
:
11px
;
}
@media
screen
and
(
min-width
:
375px
)
{
.savings-message
{
font-size
:
13px
;
margin-left
:
16px
;
}
}
}
.
v2
.continue-link
,
.v2
input
,
.v2
button
,
.v2
a
{
.
complete-program-dashboard-button
:hover
{
width
:
100%
;
background
:
#009b00
!
important
;
}
}
.v2
img
{
.complete-program-dashboard-span
{
display
:
none
;
display
:
block
;
float
:
right
;
}
}
.
v2
.deco-divider
{
.
complete-program-dashboard-div
{
display
:
none
;
display
:
none
;
}
float
:
right
;
width
:
auto
;
.v2
.visual-reference
{
max-width
:
420px
;
width
:
38%
;
margin
:
0
0
10px
0
;
}
@media
(
min-width
:
420px
)
{
.v2
button
{
height
:
45px
;
font-size
:
16px
!
important
;
}
}
@media
(
min-width
:
768px
)
{
.v2.register-choice-certificate
,
.v2.register-choice-continue
,
.v2.deco-divider
{
width
:
46
.5%
!
important
;
display
:
inline-block
;
min-height
:
270px
;
}
.v2.register-choice-v2-donate
.wrapper-copy-inline
{
display
:
flex
!
important
;
}
.v2.register-choice-v2-donate
.copy-inline
{
width
:
40%
!
important
;
}
.v2.register-choice-v2-donate
.list-actions
{
margin-top
:
0
!
important
;
text-align
:
right
!
important
;
}
.v2
.wrapper-copy-inline
.wrapper-copy
{
width
:
100%
!
important
;
}
.v2
input
,
.v2
a
{
font-size
:
15px
!
important
;
}
.v2
.continue-link
,
.v2.register-choice-certificate
button
,
.v2.register-choice-certificate
input
{
margin-top
:
20px
;
width
:
initial
;
}
.v2.register-choice-v2-donate
a
{
width
:
100%
!
important
;
}
.v2.register-choice-view
{
height
:
250px
;
}
.v2
img
{
display
:
initial
;
}
.v2.register-choice
{
margin
:
0
2%
20px
0
;
}
.v2.register-choice-continue
.wrapper-copy-inline
.wrapper-copy
,
.v2.register-choice-certificate
.wrapper-copy-inline
.wrapper-copy
{
width
:
60%
;
}
.v2.register-choice-view
.wrapper-copy-inline
.wrapper-copy
{
width
:
100%
;
}
.v2.register-choice
{
padding
:
15px
!
important
;
}
.v2.register-choice-continue
.wrapper-copy-inline
.wrapper-copy
,
.v2.register-choice-certificate
.wrapper-copy-inline
.wrapper-copy
{
width
:
60%
;
}
.v2.register-choice
{
padding
:
20px
!
important
;
}
.v2.register-choice.register-choice-view
{
margin-right
:
0
;
}
.v2.register-choice
.list-actions
:last-child
{
float
:
left
;
width
:
100%
;
margin-top
:
0
;
}
.v2.register-choice
.action-select
{
width
:
100%
!
important
;
}
.v2
.continue-link
:hover
,
.v2
.continue-link
:focus
{
background-color
:
#d7548e
!
important
;
color
:
white
!
important
;
text-decoration
:
none
;
}
.v2
.continue-link
:hover
{
cursor
:
pointer
;
}
.v2
.copy
li
{
margin-bottom
:
5px
;
}
.v2.register-choice
.copy-inline
{
width
:
100%
;
}
.v2
.register-choice-view
{
border-color
:
#2991c3
!
important
;
}
.v2
.visual-reference
{
vertical-align
:
top
;
}
.v2
.wrapper-copy-inline
.wrapper-copy
ul
{
margin-top
:
0
;
padding-left
:
30px
;
}
.v2
.img-certificate
{
border
:
2px
solid
#009b00
!
important
;
float
:
right
;
height
:
120px
;
width
:
auto
;
margin-top
:
0
!
important
;
display
:
none
;
}
.v2
.img-donate
{
margin-top
:
0
;
float
:
right
;
border
:
2px
solid
#d7548e
!
important
;
display
:
none
;
}
.v2
.img-view
{
border
:
2px
solid
#2991c3
!
important
;
}
.v2.register-choice
.title
{
width
:
100%
;
margin-bottom
:
20px
;
}
.v2.register-choice.register-choice-view
.action-select
{
border
:
1px
solid
transparent
!
important
;
border-radius
:
3px
;
}
.v2.register-choice.register-choice-view
.action-select
button
{
border
:
1px
solid
transparent
!
important
;
}
.v2.register-choice.register-choice-view
.action-select
:hover
{
border
:
1px
solid
#0075b4
!
important
;
}
.v2.deco-divider
{
width
:
3%
!
important
;
box-sizing
:
border-box
;
float
:
left
;
display
:
inline-block
;
height
:
250px
;
margin
:
0
0
40px
0
!
important
;
border-left
:
4px
solid
#f5f5f5
!
important
;
border-top
:none
!
important
;
.copy
{
position
:
absolute
;
top
:
110px
!
important
;
left
:
calc
(
50%
-
40px
)
!
important
;
margin-left
:
20px
;
background
:
white
;
text-align
:
center
;
color
:
#474747
;
width
:
10px
;
padding
:
0
!
important
;
}
}
}
@media
(
min-width
:
835px
)
{
.v2.register-choice-certificate
,
.v2.register-choice-continue
,
.v2.deco-divider
{
min-height
:
250px
;
}
}
@media
(
min-width
:
1024px
)
{
.v2
.continue-link
{
width
:
55%
;
}
.v2.deco-divider
.copy
{
margin-left
:
15px
;
}
}
@media
(
min-width
:
1096px
)
{
.v2.register-choice-certificate
,
.v2.register-choice-continue
,
.v2.deco-divider
{
min-height
:
260px
;
}
.v2
.img-certificate
,
.v2
.img-donate
{
margin-top
:
10px
;
display
:
initial
;
}
.v2
.continue-link
,
.v2.register-choice-certificate
button
,
.v2.register-choice-certificate
input
{
margin-top
:
-22px
!
important
;
}
}
}
lms/templates/dashboard/_dashboard_course_listing.html
View file @
dd8e879e
...
@@ -326,17 +326,31 @@ from util.course import get_link_for_about_page, get_encoded_course_sharing_utm_
...
@@ -326,17 +326,31 @@ from util.course import get_link_for_about_page, get_encoded_course_sharing_utm_
%endif
%endif
% if related_programs:
% if related_programs:
<div
class=
"message message-related-programs is-shown"
>
<div
class=
"message message-related-programs is-shown"
>
<span
class=
"related-programs-preface"
tabindex=
"0"
>
${_('Related Programs')}:
</span>
<span
class=
"related-programs-preface"
tabindex=
"0"
>
${_('Related Programs')}:
</span>
<ul>
<ul>
% for program in related_programs:
% for program in related_programs:
<li>
<li>
<span
class=
"category-icon ${program['type'].lower()}-icon"
aria-hidden=
"true"
></span>
<span
class=
"category-icon ${program['type'].lower()}-icon"
aria-hidden=
"true"
></span>
<span><a
href=
"${program['detail_url']}"
>
${u'{title} {type}'.format(title=program['title'], type=program['type'])}
</a></span>
<span><a
href=
"${program['detail_url']}"
>
${u'{title} {type}'.format(title=program['title'], type=program['type'])}
</a></span>
</li>
</li>
% endfor
% endfor
</ul>
</ul>
</div>
% if program_data.get('is_learner_eligible_for_one_click_purchase'):
<div
class=
"complete-program-dashboard-div"
><span
class=
"complete-program-dashboard-span"
>
${_('Buy all remaining courses in this program and save 10%')}
</span>
<a
href=
"${urls.get('completeProgramURL')}"
class=
"btn-brand btn cta-primary upgrade-button complete-program-dashboard-button"
>
${_('Upgrade All Remaining Courses (')}
% if program_data.get('discount_data', {}).get('is_discounted'):
<span
class=
'list-price'
>
${_('{"{0:.2f}".format(a)}'.format(program_data['discount_data']['total_incl_tax_excl_discounts'], 2))}
</span>
% endif
${_(' ${price:.2f} {currency} ) '.format(price=program_data.get('full_program_price'),
currency=program_data.get('discount_data', {}).get('currency')))}
</a>
</div>
% endif
</div>
% endif
% endif
% if cert_status:
% if cert_status:
...
...
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