Files
edx-platform/lms/templates/dashboard.html
2022-05-25 12:01:42 -04:00

405 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<%page expression_filter="h"/>
<%inherit file="main.html" />
<%def name="online_help_token()"><% return "learnerdashboard" %></%def>
<%namespace name='static' file='static_content.html'/>
<%!
import pytz
import six
from datetime import datetime, timedelta
from django.urls import reverse
from django.utils.translation import gettext as _
from django.template import RequestContext
from common.djangoapps.entitlements.models import CourseEntitlement
from common.djangoapps.third_party_auth import pipeline
from common.djangoapps.util.date_utils import strftime_localized
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_string
from openedx.core.djangolib.markup import HTML, Text
from common.djangoapps.student.models import CourseEnrollment
%>
<%
cert_name_short = settings.CERT_NAME_SHORT
cert_name_long = settings.CERT_NAME_LONG
%>
<%block name="pagetitle">${_("Dashboard")}</%block>
<%block name="bodyclass">view-dashboard is-authenticated</%block>
<%block name="header_extras">
% for template_name in ["donation"]:
<script type="text/template" id="${template_name}-tpl">
<%static:include path="dashboard/${template_name}.underscore" />
</script>
% endfor
</%block>
<%block name="js_extra">
<script src="${static.url('js/commerce/credit.js')}"></script>
<script type="text/javascript" src="${static.url('js/learner_dashboard/certificate_api.js')}"></script>
<%static:js group='dashboard'/>
<script type="text/javascript">
$(document).ready(function() {
edx.dashboard.legacy.init({
dashboard: "${reverse('dashboard') | n, js_escaped_string}",
signInUser: "${reverse('signin_user') | n, js_escaped_string}",
changeEmailSettings: "${reverse('change_email_settings') | n, js_escaped_string}",
sendAccountActivationEmail: "${reverse('send_account_activation_email') | n, js_escaped_string}"
});
});
</script>
<%static:webpack entry="UnenrollmentFactory">
UnenrollmentFactory({
urls: {
dashboard: "${reverse('dashboard') | n, js_escaped_string}",
signInUser: "${reverse('signin_user') | n, js_escaped_string}",
changeEmailSettings: "${reverse('change_email_settings') | n, js_escaped_string}",
browseCourses: "${marketing_link('COURSES') | n, js_escaped_string}"
},
isEdx: false
});
</%static:webpack>
<%static:webpack entry="EntitlementUnenrollmentFactory">
## Wait until the document is fully loaded before initializing the EntitlementUnenrollmentView
## to ensure events are setup correctly.
$(document).ready(function() {
EntitlementUnenrollmentFactory({
dashboardPath: "${reverse('dashboard') | n, js_escaped_string}",
signInPath: "${reverse('signin_user') | n, js_escaped_string}",
browseCourses: "${marketing_link('COURSES') | n, js_escaped_string}",
isEdx: false
});
});
</%static:webpack>
% if settings.FEATURES.get('ENABLE_DASHBOARD_SEARCH'):
<%static:require_module module_name="course_search/js/dashboard_search_factory" class_name="DashboardSearchFactory">
DashboardSearchFactory();
</%static:require_module>
% endif
% if redirect_message:
<%static:require_module module_name="js/views/message_banner" class_name="MessageBannerView">
var banner = new MessageBannerView({urgency: 'low', type: 'warning'});
$('#content').prepend(banner.$el);
banner.showMessage(${redirect_message | n, dump_js_escaped_json})
</%static:require_module>
% endif
% if recovery_email_message:
<%static:require_module module_name="js/views/message_banner" class_name="MessageBannerView">
var banner = new MessageBannerView({urgency: 'low', type: 'warning', hideCloseBtn: false, isRecoveryEmailMsg: true});
$('#content').prepend(banner.$el);
banner.showMessage(${recovery_email_message | n, dump_js_escaped_json})
</%static:require_module>
% endif
% if recovery_email_activation_message:
<%static:require_module module_name="js/views/message_banner" class_name="MessageBannerView">
var banner = new MessageBannerView({urgency: 'low', type: 'warning', isRecoveryEmailMsg: true});
$('#content').prepend(banner.$el);
banner.showMessage(${recovery_email_activation_message | n, dump_js_escaped_json})
</%static:require_module>
% endif
% if enterprise_learner_portal_enabled_message:
<%static:require_module module_name="js/views/message_banner" class_name="MessageBannerView">
var banner = new MessageBannerView({urgency: 'low', type: 'warning', isLearnerPortalEnabled: true});
$('#content').prepend(banner.$el);
banner.showMessage(${enterprise_learner_portal_enabled_message | n, dump_js_escaped_json})
</%static:require_module>
% endif
</%block>
<div class="dashboard-notifications" tabindex="-1">
%if banner_account_activation_message:
<div class="dashboard-banner">
${banner_account_activation_message | n, decode.utf8}
</div>
%endif
%if enrollment_message:
<div class="dashboard-banner">
${enrollment_message | n, decode.utf8}
</div>
%endif
%if enterprise_message:
<div class="dashboard-banner">
${ enterprise_message | n, decode.utf8 }
</div>
%endif
%if account_activation_messages:
<div class="activation-message-container">
% for account_activation_message in account_activation_messages:
<div class="account-activation ${account_activation_message.tags}" role="alert" aria-label="Account Activation Message" tabindex="-1">
<div class="message-copy" >
${ account_activation_message | n, decode.utf8 }
</div>
</div>
% endfor
</div>
%endif
</div>
<main id="main" aria-label="Content" tabindex="-1">
<div class="dashboard" id="dashboard-main">
<div class="main-container">
<div class="my-courses" id="my-courses">
% if display_dashboard_courses:
<%include file="learner_dashboard/_dashboard_navigation_courses.html"/>
% endif
% if len(course_entitlements + course_enrollments) > 0:
<ul class="listing-courses">
<%
share_settings = configuration_helpers.get_value(
'SOCIAL_SHARING_SETTINGS',
getattr(settings, 'SOCIAL_SHARING_SETTINGS', {})
)
%>
% for dashboard_index, enrollment in enumerate(course_entitlements + course_enrollments):
<%
# Check if the course run is an entitlement and if it has an associated session
entitlement = enrollment if isinstance(enrollment, CourseEntitlement) else None
entitlement_session = entitlement.enrollment_course_run if entitlement else None
entitlement_days_until_expiration = entitlement.get_days_until_expiration() if entitlement else None
entitlement_expiration = datetime.now(tz=pytz.UTC) + timedelta(days=entitlement_days_until_expiration) if (entitlement and entitlement_days_until_expiration < settings.ENTITLEMENT_EXPIRED_ALERT_PERIOD) else None
entitlement_expiration_date = strftime_localized(entitlement_expiration, 'SHORT_DATE') if entitlement and entitlement_expiration else None
entitlement_expired_at = strftime_localized(entitlement.expired_at_datetime, 'SHORT_DATE') if entitlement and entitlement.expired_at_datetime else None
is_fulfilled_entitlement = True if entitlement and entitlement_session else False
is_unfulfilled_entitlement = True if entitlement and not entitlement_session else False
entitlement_available_sessions = []
if entitlement:
# Grab the available, enrollable sessions for a given entitlement and scrape them for relevant attributes
entitlement_available_sessions = [{
'session_id': course['key'],
'enrollment_end': course['enrollment_end'],
'pacing_type': course['pacing_type'],
'advertised_start': CourseOverview.get_from_id(CourseKey.from_string(course['key'])).advertised_start,
'start': CourseOverview.get_from_id(CourseKey.from_string(course['key'])).start,
'end': CourseOverview.get_from_id(CourseKey.from_string(course['key'])).end,
} for course in course_entitlement_available_sessions[str(entitlement.uuid)]]
if is_fulfilled_entitlement:
# If the user has a fulfilled entitlement, pass through the entitlements CourseEnrollment object
enrollment = entitlement_session
else:
# If the user has an unfulfilled entitlement, pass through a bare CourseEnrollment object to populate card with metadata
pseudo_session = unfulfilled_entitlement_pseudo_sessions[str(entitlement.uuid)]
if not pseudo_session:
continue
pseudo_key = pseudo_session['key']
if not isinstance(pseudo_key, CourseKey):
pseudo_key = CourseKey.from_string(pseudo_session['key'])
enrollment = CourseEnrollment(user=user, course=CourseOverview.get_from_id(pseudo_key), mode=pseudo_session['type'])
# We only show email settings for entitlement cards if the entitlement has an associated enrollment
show_email_settings = is_fulfilled_entitlement and (entitlement_session.course_id in show_email_settings_for)
course_overview = enrollment.course_overview
else:
show_email_settings = (enrollment.course_id in show_email_settings_for)
course_overview = CourseOverview.get_from_id(enrollment.course_id)
session_id = enrollment.course_id
show_courseware_link = show_courseware_links_for.get(session_id, False)
cert_status = cert_statuses.get(session_id)
can_refund_entitlement = entitlement and entitlement.is_entitlement_refundable()
partner_managed_enrollment = enrollment.mode == 'masters'
# checks if we can unenroll based on the value of partner_managed_enrollment
can_unenroll_partner_managed_enrollment = False if partner_managed_enrollment else (not cert_status)
# checks if we can unenroll based on the value of unfulfilled_entitlement
can_unenroll_unfulfilled_entitlement = cert_status.get('can_unenroll') if cert_status and not unfulfilled_entitlement else False
# compares the three different parameters by which we can unenroll
can_unenroll = (can_unenroll_partner_managed_enrollment or can_unenroll_unfulfilled_entitlement) and not disable_unenrollment
credit_status = credit_statuses.get(session_id)
course_mode_info = all_course_modes.get(session_id)
is_paid_course = True if entitlement else (session_id in enrolled_courses_either_paid)
is_course_voucher_refundable = (session_id in enrolled_courses_voucher_refundable)
course_requirements = courses_requirements_not_met.get(session_id)
related_programs = inverted_programs.get(six.text_type(entitlement.course_uuid if is_unfulfilled_entitlement else session_id))
show_consent_link = (session_id in consent_required_courses)
resume_button_url = resume_button_urls[dashboard_index]
%>
<%include file='dashboard/_dashboard_course_listing.html' args='course_overview=course_overview, course_card_index=dashboard_index, enrollment=enrollment, enrollments_fbe_is_on=enrollments_fbe_is_on, is_unfulfilled_entitlement=is_unfulfilled_entitlement, is_fulfilled_entitlement=is_fulfilled_entitlement, entitlement=entitlement, entitlement_session=entitlement_session, entitlement_available_sessions=entitlement_available_sessions, entitlement_expiration_date=entitlement_expiration_date, entitlement_expired_at=entitlement_expired_at, show_courseware_link=show_courseware_link, cert_status=cert_status, can_refund_entitlement=can_refund_entitlement, can_unenroll=can_unenroll, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, is_paid_course=is_paid_course, is_course_voucher_refundable=is_course_voucher_refundable, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user, related_programs=related_programs, display_course_modes_on_dashboard=display_course_modes_on_dashboard, show_consent_link=show_consent_link, enterprise_customer_name=enterprise_customer_name, resume_button_url=resume_button_url, partner_managed_enrollment=partner_managed_enrollment' />
% endfor
% if show_load_all_courses_link:
<br/>
${len(course_enrollments)} ${_("results successfully populated,")}
<a href="${reverse('dashboard')}?course_limit=None">
${_("Click to load all enrolled courses")}
</a>
% endif
</ul>
% else:
<div class="empty-dashboard-message">
% if display_dashboard_courses:
<p>${_("You are not enrolled in any courses yet.")}</p>
% if empty_dashboard_message:
<p class="custom-message">${empty_dashboard_message | n, decode.utf8}</p>
%endif
% if settings.FEATURES.get('COURSES_ARE_BROWSABLE'):
<a class="btn btn-primary" href="${marketing_link('COURSES')}">
${_("Explore courses")}
</a>
%endif
% else:
<p>${_("Activate your account!")}</p>
<p class="custom-message">${ activate_account_message | n, decode.utf8 }</p>
% endif
</div>
% endif
% if staff_access and len(errored_courses) > 0:
<div id="course-errors">
<h2>${_("Course-loading errors")}</h2>
% for course_dir, errors in errored_courses.items():
<h3>${course_dir}</h3>
<ul>
% for (msg, err) in errors:
<li>${msg}
<ul><li><pre>${err}</pre></li></ul>
</li>
% endfor
</ul>
% endfor
</div>
% endif
</div>
</div>
<div class="side-container" role="complementary" aria-label="messages">
%if display_sidebar_account_activation_message:
<div class="sidebar-notification">
<%include file="${static.get_template_path('registration/account_activation_sidebar_notice.html')}" />
</div>
%endif
% if settings.FEATURES.get('ENABLE_DASHBOARD_SEARCH'):
<div id="dashboard-search-bar" class="search-bar dashboard-search-bar" role="search" aria-label="Dashboard">
<form class="search-form">
<label for="dashboard-search-input">${_('Search Your Courses')}</label>
<div class="search-field-wrapper">
<input id="dashboard-search-input" type="text" class="search-field"/>
<button type="submit" class="search-button" title="${_('Search')}">
<span class="icon fa fa-search" aria-hidden="true"></span>
</button>
<button type="button" class="cancel-button" title="${_('Clear search')}">
<span class="icon fa fa-remove" aria-hidden="true"></span>
</button>
</div>
</form>
</div>
<div id="dashboard-search-results" class="search-results dashboard-search-results"></div>
% endif
<%block name="skip_links">
% if settings.FEATURES.get('ENABLE_ANNOUNCEMENTS'):
<a id="announcements-skip" class="nav-skip sr-only sr-only-focusable" href="#announcements">${_("Skip to list of announcements")}</a>
% endif
</%block>
% if settings.FEATURES.get('ENABLE_ANNOUNCEMENTS'):
<%include file='dashboard/_dashboard_announcements.html' />
% endif
</div>
</div>
</main>
%if show_account_activation_popup:
<div id="activate-account-modal" class="modal activate-account-modal" aria-hidden="true" tabindex=0 >
<div class="inner-wrapper" role="dialog" aria-labelledby="activate-account-modal-title" aria-live="polite">
<h3>
${_("Activate your account so you can log back in")}
<span class="sr">,
## Translators: this text gives status on if the modal interface (a menu or piece of UI that takes the full focus of the screen) is open or not
${_("window open")}
</span>
</h3>
<p class="activate-account-modal-body">${Text(_("We sent an email to {strong_start}{email}{strong_end} with a link to activate your account. Cant find it? Check your spam folder or {link_start}resend the email{link_end}.")).format(
strong_start=HTML('<strong>'),
email=user.email,
strong_end=HTML('</strong>'),
link_start=HTML('<a href="#" id="send_cta_email" >'),
link_end=HTML('</a>')
)}
</p>
<div class="activate-account-modal-button">
<button class="btn btn-primary" id="button">
${Text(_("Continue to {platform_name}")).format(platform_name=settings.PLATFORM_NAME)}
<svg style="vertical-align:bottom" width="24" height="24" viewBox="0 0 24 24" fill="white" xmlns="http://www.w3.org/2000/svg"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8-8-8z"/></svg>
</button>
</div>
</div>
</div>
%endif
<div id="email-settings-modal" class="modal" aria-hidden="true">
<div class="inner-wrapper" role="dialog" aria-labelledby="email-settings-title">
<button class="close-modal">
<span class="icon fa fa-remove" aria-hidden="true"></span>
<span class="sr">
## Translators: this is a control to allow users to exit out of this modal interface (a menu or piece of UI that takes the full focus of the screen)
${_("Close")}
</span>
</button>
<header>
<h2 id="email-settings-title">
${Text(_("Email Settings for {course_number}")).format(course_number=HTML('<span id="email_settings_course_number"></span>'))}
<span class="sr">,
## Translators: this text gives status on if the modal interface (a menu or piece of UI that takes the full focus of the screen) is open or not
${_("window open")}
</span>
</h2>
<hr/>
</header>
<form id="email_settings_form" method="post">
<input name="course_id" id="email_settings_course_id" type="hidden" />
<label><input type="checkbox" id="receive_emails" name="receive_emails" />${_("Receive course emails")} </label>
<div class="submit">
<input type="submit" id="submit" value="${_("Save Settings")}" />
</div>
</form>
</div>
</div>
<div id="unenroll-modal" class="modal unenroll-modal" aria-hidden="true">
<div class="inner-wrapper" role="dialog" aria-labelledby="unenrollment-modal-title" aria-live="polite">
<button class="close-modal">
<span class="icon fa fa-remove" aria-hidden="true"></span>
<span class="sr">
## Translators: this is a control to allow users to exit out of this modal interface (a menu or piece of UI that takes the full focus of the screen)
${_("Close")}
</span>
</button>
<header class="unenroll-header">
<h2 id="unenrollment-modal-title">
<span id='track-info'></span>
<span id='refund-info'></span>
<span class="sr">,
## Translators: this text gives status on if the modal interface (a menu or piece of UI that takes the full focus of the screen) is open or not
${_("window open")}
</span>
</h2>
<hr/>
</header>
<div id="unenroll_error" class="modal-form-error"></div>
<form id="unenroll_form" method="post" data-remote="true" action="${reverse('change_enrollment')}">
<input name="course_id" id="unenroll_course_id" type="hidden" />
<input name="enrollment_action" type="hidden" value="unenroll" />
<div class="submit">
<input class="submit-button" name="submit" type="submit" value="${_("Unenroll")}" />
</div>
</form>
</div>
</div>
<%include file="dashboard/_dashboard_entitlement_unenrollment_modal.html"/>