Example #1
0
def check_content_start_date_for_masquerade_user(course_key,
                                                 user,
                                                 request,
                                                 course_start,
                                                 chapter_start=None,
                                                 section_start=None):
    """
    Add a warning message if the masquerade user would not have access to this content
    due to the content start date being in the future.
    """
    now = datetime.now(utc)
    most_future_date = course_start
    if chapter_start and section_start:
        most_future_date = max(course_start, chapter_start, section_start)
    _is_masquerading = get_course_masquerade(user, course_key)
    if now < most_future_date and _is_masquerading:
        group_masquerade = is_masquerading_as_student(user, course_key)
        specific_student_masquerade = is_masquerading_as_specific_student(
            user, course_key)
        is_staff = has_staff_roles(user, course_key)
        if group_masquerade or (specific_student_masquerade and not is_staff):
            PageLevelMessages.register_warning_message(
                request,
                HTML(
                    _('This user does not have access to this content because \
                        the content start date is in the future')),
                once_only=True)
Example #2
0
def register_course_expired_message(request, course):
    """
    Add a banner notifying the user of the user course expiration date if it exists.
    """
    if not CourseDurationLimitConfig.enabled_for_enrollment(user=request.user, course_key=course.id):
        return

    expiration_date = get_user_course_expiration_date(request.user, course)
    if not expiration_date:
        return

    if is_masquerading_as_student(request.user, course.id) and timezone.now() > expiration_date:
        upgrade_message = _('This learner does not have access to this course. '
                            'Their access expired on {expiration_date}.')
        PageLevelMessages.register_warning_message(
            request,
            HTML(upgrade_message).format(
                expiration_date=expiration_date.strftime('%b %-d')
            )
        )
    else:
        enrollment = CourseEnrollment.get_enrollment(request.user, course.id)
        if enrollment is None:
            return

        upgrade_deadline = enrollment.upgrade_deadline
        if upgrade_deadline is None:
            return
        now = timezone.now()
        course_upgrade_deadline = enrollment.course_upgrade_deadline
        if now > upgrade_deadline:
            upgrade_deadline = course_upgrade_deadline

        expiration_message = _('{strong_open}Audit Access Expires {expiration_date}{strong_close}'
                               '{line_break}You lose all access to this course, including your progress, on '
                               '{expiration_date}.')
        upgrade_deadline_message = _('{line_break}Upgrade by {upgrade_deadline} to get unlimited access to the course '
                                     'as long as it exists on the site. {a_open}Upgrade now{sronly_span_open} to '
                                     'retain access past {expiration_date}{span_close}{a_close}')
        full_message = expiration_message
        if now < course_upgrade_deadline:
            full_message += upgrade_deadline_message

        PageLevelMessages.register_info_message(
            request,
            Text(full_message).format(
                a_open=HTML('<a href="{upgrade_link}">').format(
                    upgrade_link=verified_upgrade_deadline_link(user=request.user, course=course)
                ),
                sronly_span_open=HTML('<span class="sr-only">'),
                sighted_only_span_open=HTML('<span aria-hidden="true">'),
                span_close=HTML('</span>'),
                a_close=HTML('</a>'),
                expiration_date=expiration_date.strftime('%b. %-d, %Y'),
                strong_open=HTML('<strong>'),
                strong_close=HTML('</strong>'),
                line_break=HTML('<br>'),
                upgrade_deadline=upgrade_deadline.strftime('%b. %-d, %Y')
            )
        )
Example #3
0
 def _decorated(request, *args, **kwargs):  # pylint: disable=missing-docstring
     if waffle().is_enabled(DISPLAY_MAINTENANCE_WARNING):
         if hasattr(settings, 'MAINTENANCE_BANNER_TEXT') and settings.MAINTENANCE_BANNER_TEXT:
             # The waffle switch is enabled and the banner text is defined
             # and non-empty.  We can now register the message:
             PageLevelMessages.register_warning_message(request, settings.MAINTENANCE_BANNER_TEXT)
     return func(request, *args, **kwargs)
Example #4
0
def _enforce_password_policy_compliance(request, user):
    try:
        password_policy_compliance.enforce_compliance_on_login(user, request.POST.get('password'))
    except password_policy_compliance.NonCompliantPasswordWarning as e:
        # Allow login, but warn the user that they will be required to reset their password soon.
        PageLevelMessages.register_warning_message(request, e.message)
    except password_policy_compliance.NonCompliantPasswordException as e:
        # Prevent the login attempt.
        raise AuthFailedError(e.message)
Example #5
0
def _enforce_password_policy_compliance(request, user):  # lint-amnesty, pylint: disable=missing-function-docstring
    try:
        password_policy_compliance.enforce_compliance_on_login(user, request.POST.get('password'))
    except password_policy_compliance.NonCompliantPasswordWarning as e:
        # Allow login, but warn the user that they will be required to reset their password soon.
        PageLevelMessages.register_warning_message(request, str(e))
    except password_policy_compliance.NonCompliantPasswordException as e:
        AUDIT_LOG.info("Password reset initiated for email %s.", user.email)
        send_password_reset_email_for_user(user, request)
        # Prevent the login attempt.
        raise AuthFailedError(HTML(str(e)), error_code=e.__class__.__name__)  # lint-amnesty, pylint: disable=raise-missing-from
Example #6
0
    def render(self, request):
        """
        Render the index page.
        """
        self._redirect_if_needed_to_pay_for_course()
        self._prefetch_and_bind_course(request)

        if self.course.has_children_at_depth(CONTENT_DEPTH):
            self._reset_section_to_exam_if_required()
            self.chapter = self._find_chapter()
            self.section = self._find_section()

            if self.chapter and self.section:
                self._redirect_if_not_requested_section()
                self._save_positions()
                self._prefetch_and_bind_section()
                self._redirect_to_learning_mfe()

            check_content_start_date_for_masquerade_user(
                self.course_key, self.effective_user, request,
                self.course.start, self.chapter.start, self.section.start)

        if not request.user.is_authenticated:
            qs = six.moves.urllib.parse.urlencode({
                'course_id': self.course_key,
                'enrollment_action': 'enroll',
                'email_opt_in': False,
            })

            allow_anonymous = check_public_access(self.course,
                                                  [COURSE_VISIBILITY_PUBLIC])

            if not allow_anonymous:
                PageLevelMessages.register_warning_message(
                    request,
                    Text(
                        _(u"You are not signed in. To see additional course content, {sign_in_link} or "
                          u"{register_link}, and enroll in this course.")).
                    format(
                        sign_in_link=HTML(
                            u'<a href="{url}">{sign_in_label}</a>').format(
                                sign_in_label=_('sign in'),
                                url='{}?{}'.format(reverse('signin_user'), qs),
                            ),
                        register_link=HTML(
                            u'<a href="/{url}">{register_label}</a>').format(
                                register_label=_('register'),
                                url=u'{}?{}'.format(reverse('register_user'),
                                                    qs),
                            ),
                    ))

        return render_to_response('courseware/courseware.html',
                                  self._create_courseware_context(request))
Example #7
0
def show_reference_template(request, template):
    """
    Shows the specified template as an HTML page. This is used only in
    debug mode to allow the UX team to produce and work with static
    reference templates.

    e.g. http://localhost:8000/template/ux/reference/index.html
    shows the template from ux/reference/index.html

    Note: dynamic parameters can also be passed to the page.
    e.g. /template/ux/reference/index.html?name=Foo
    """
    try:
        uses_pattern_library = u'/pattern-library/' in request.path
        is_v1 = u'/v1/' in request.path
        uses_bootstrap = not uses_pattern_library and not is_v1
        context = {
            'request': request,
            'disable_courseware_js': not is_v1,
            'uses_pattern_library': uses_pattern_library,
            'uses_bootstrap': uses_bootstrap,
        }
        context.update(request.GET.dict())

        # Support dynamic rendering of messages
        if request.GET.get('alert'):
            PageLevelMessages.register_info_message(request,
                                                    request.GET.get('alert'))
        if request.GET.get('success'):
            PageLevelMessages.register_success_message(
                request, request.GET.get('success'))
        if request.GET.get('warning'):
            PageLevelMessages.register_warning_message(
                request, request.GET.get('warning'))
        if request.GET.get('error'):
            PageLevelMessages.register_error_message(request,
                                                     request.GET.get('error'))

        # Add some messages to the course skeleton pages
        if u'course-skeleton.html' in request.path:
            PageLevelMessages.register_info_message(
                request, _('This is a test message'))
            PageLevelMessages.register_success_message(
                request, _('This is a success message'))
            PageLevelMessages.register_warning_message(
                request, _('This is a test warning'))
            PageLevelMessages.register_error_message(request,
                                                     _('This is a test error'))

        return render_to_response(template, context)
    except TopLevelLookupException:
        return HttpResponseNotFound(
            'Missing template {template}'.format(template=template))
Example #8
0
def _enforce_password_policy_compliance(request, user):
    try:
        password_policy_compliance.enforce_compliance_on_login(
            user, request.POST.get('password'))
    except password_policy_compliance.NonCompliantPasswordWarning as e:
        # Allow login, but warn the user that they will be required to reset their password soon.
        PageLevelMessages.register_warning_message(request, six.text_type(e))
    except password_policy_compliance.NonCompliantPasswordException as e:
        send_password_reset_email_for_user(user, request)
        # Prevent the login attempt.
        raise AuthFailedError(HTML(six.text_type(e)),
                              error_code=e.__class__.__name__)
Example #9
0
def show_reference_template(request, template):
    """
    Shows the specified template as an HTML page. This is used only in
    debug mode to allow the UX team to produce and work with static
    reference templates.

    e.g. http://localhost:8000/template/ux/reference/index.html
    shows the template from ux/reference/index.html

    Note: dynamic parameters can also be passed to the page.
    e.g. /template/ux/reference/index.html?name=Foo
    """
    try:
        is_v1 = '/v1/' in request.path
        uses_bootstrap = not is_v1
        context = {
            'request': request,
            'uses_bootstrap': uses_bootstrap,
        }
        context.update(request.GET.dict())

        # Support dynamic rendering of messages
        if request.GET.get('alert'):
            PageLevelMessages.register_info_message(request,
                                                    request.GET.get('alert'))
        if request.GET.get('success'):
            PageLevelMessages.register_success_message(
                request, request.GET.get('success'))
        if request.GET.get('warning'):
            PageLevelMessages.register_warning_message(
                request, request.GET.get('warning'))
        if request.GET.get('error'):
            PageLevelMessages.register_error_message(request,
                                                     request.GET.get('error'))

        # Add some messages to the course skeleton pages
        if 'course-skeleton.html' in request.path:
            PageLevelMessages.register_info_message(
                request, _('This is a test message'))
            PageLevelMessages.register_success_message(
                request, _('This is a success message'))
            PageLevelMessages.register_warning_message(
                request, _('This is a test warning'))
            PageLevelMessages.register_error_message(request,
                                                     _('This is a test error'))

        return render_to_response(template, context)
    except TemplateDoesNotExist:
        return HttpResponseNotFound(
            f'Missing template {bleach.clean(template, strip=True)}')
Example #10
0
    def render(self, request):
        """
        Render the index page.
        """
        self._redirect_if_needed_to_pay_for_course()
        self._prefetch_and_bind_course(request)

        if self.course.has_children_at_depth(CONTENT_DEPTH):
            self._reset_section_to_exam_if_required()
            self.chapter = self._find_chapter()
            self.section = self._find_section()

            if self.chapter and self.section:
                self._redirect_if_not_requested_section()
                self._save_positions()
                self._prefetch_and_bind_section()

            check_content_start_date_for_masquerade_user(self.course_key, self.effective_user, request,
                                                         self.course.start, self.chapter.start, self.section.start)

        if not request.user.is_authenticated:
            qs = urllib.urlencode({
                'course_id': self.course_key,
                'enrollment_action': 'enroll',
                'email_opt_in': False,
            })

            allow_anonymous = allow_public_access(self.course, [COURSE_VISIBILITY_PUBLIC])

            if not allow_anonymous:
                PageLevelMessages.register_warning_message(
                    request,
                    Text(_(u"You are not signed in. To see additional course content, {sign_in_link} or "
                           u"{register_link}, and enroll in this course.")).format(
                        sign_in_link=HTML(u'<a href="{url}">{sign_in_label}</a>').format(
                            sign_in_label=_('sign in'),
                            url='{}?{}'.format(reverse('signin_user'), qs),
                        ),
                        register_link=HTML(u'<a href="/{url}">{register_label}</a>').format(
                            register_label=_('register'),
                            url=u'{}?{}'.format(reverse('register_user'), qs),
                        ),
                    )
                )

        return render_to_response('courseware/courseware.html', self._create_courseware_context(request))
Example #11
0
    def render(self, request):
        """
        Render the index page.
        """
        self._redirect_if_needed_to_enter_university_id()
        self._redirect_if_needed_to_pay_for_course()
        self._prefetch_and_bind_course(request)

        if self.course.has_children_at_depth(CONTENT_DEPTH):
            self._reset_section_to_exam_if_required()
            self.chapter = self._find_chapter()
            self.section = self._find_section()

            if self.chapter and self.section:
                self._redirect_if_not_requested_section()
                self._save_positions()
                self._prefetch_and_bind_section()

        if not request.user.is_authenticated:
            qs = urllib.urlencode({
                'course_id': self.course_key,
                'enrollment_action': 'enroll',
                'email_opt_in': False,
            })

            PageLevelMessages.register_warning_message(
                request,
                Text(
                    _("You are not signed in. To see additional course content, {sign_in_link} or "
                      "{register_link}, and enroll in this course.")).format(
                          sign_in_link=HTML(
                              '<a href="{url}">{sign_in_label}</a>').format(
                                  sign_in_label=_('sign in'),
                                  url='{}?{}'.format(reverse('signin_user'),
                                                     qs),
                              ),
                          register_link=HTML(
                              '<a href="/{url}">{register_label}</a>').format(
                                  register_label=_('register'),
                                  url='{}?{}'.format(reverse('register_user'),
                                                     qs),
                              ),
                      ))

        return render_to_response('courseware/courseware.html',
                                  self._create_courseware_context(request))
Example #12
0
def _enforce_password_policy_compliance(request, user):  # lint-amnesty, pylint: disable=missing-function-docstring
    try:
        password_policy_compliance.enforce_compliance_on_login(
            user, request.POST.get('password'))
    except password_policy_compliance.NonCompliantPasswordWarning as e:
        # Allow login, but warn the user that they will be required to reset their password soon.
        PageLevelMessages.register_warning_message(request, HTML(str(e)))
    except password_policy_compliance.NonCompliantPasswordException as e:
        # Increment the lockout counter to safguard from further brute force requests
        # if user's password has been compromised.
        if LoginFailures.is_feature_enabled():
            LoginFailures.increment_lockout_counter(user)

        AUDIT_LOG.info("Password reset initiated for email %s.", user.email)
        send_password_reset_email_for_user(user, request)

        # Prevent the login attempt.
        raise AuthFailedError(HTML(str(e)), error_code=e.__class__.__name__)  # lint-amnesty, pylint: disable=raise-missing-from
Example #13
0
def show_reference_template(request, template):
    """
    Shows the specified template as an HTML page. This is used only in
    debug mode to allow the UX team to produce and work with static
    reference templates.

    e.g. http://localhost:8000/template/ux/reference/index.html
    shows the template from ux/reference/index.html

    Note: dynamic parameters can also be passed to the page.
    e.g. /template/ux/reference/index.html?name=Foo
    """
    try:
        uses_pattern_library = u'/pattern-library/' in request.path
        is_v1 = u'/v1/' in request.path
        uses_bootstrap = not uses_pattern_library and not is_v1
        context = {
            'request': request,
            'uses_pattern_library': uses_pattern_library,
            'uses_bootstrap': uses_bootstrap,
        }
        context.update(request.GET.dict())

        # Support dynamic rendering of messages
        if request.GET.get('alert'):
            PageLevelMessages.register_info_message(request, request.GET.get('alert'))
        if request.GET.get('success'):
            PageLevelMessages.register_success_message(request, request.GET.get('success'))
        if request.GET.get('warning'):
            PageLevelMessages.register_warning_message(request, request.GET.get('warning'))
        if request.GET.get('error'):
            PageLevelMessages.register_error_message(request, request.GET.get('error'))

        # Add some messages to the course skeleton pages
        if u'course-skeleton.html' in request.path:
            PageLevelMessages.register_info_message(request, _('This is a test message'))
            PageLevelMessages.register_success_message(request, _('This is a success message'))
            PageLevelMessages.register_warning_message(request, _('This is a test warning'))
            PageLevelMessages.register_error_message(request, _('This is a test error'))

        return render_to_response(template, context)
    except TopLevelLookupException:
        return HttpResponseNotFound('Missing template {template}'.format(template=template))
def check_start_date(user, days_early_for_beta, start, course_key):
    """
    Verifies whether the given user is allowed access given the
    start date and the Beta offset for the given course.

    Returns:
        AccessResponse: Either ACCESS_GRANTED or StartDateError.
    """
    start_dates_disabled = settings.FEATURES['DISABLE_START_DATES']
    masquerading_as_student = is_masquerading_as_student(user, course_key)
    masquerading_as_specific_student = is_masquerading_as_specific_student(
        user, course_key)

    if start_dates_disabled and not masquerading_as_student:
        return ACCESS_GRANTED
    else:
        now = datetime.now(UTC)
        if start is None or in_preview_mode():
            return ACCESS_GRANTED

        effective_start = adjust_start_date(user, days_early_for_beta, start,
                                            course_key)
        if now > effective_start:
            return ACCESS_GRANTED

        if get_course_masquerade(user, course_key):
            if masquerading_as_student or (
                    masquerading_as_specific_student
                    and not has_staff_roles(user, course_key)):
                request = get_current_request()
                PageLevelMessages.register_warning_message(
                    request,
                    HTML(
                        _('This user does not have access to this content due to the content start date'
                          )),
                    once_only=True)
                return ACCESS_GRANTED

        return StartDateError(start)
Example #15
0
def check_content_start_date_for_masquerade_user(course_key, user, request, course_start,
                                                 chapter_start=None, section_start=None):
    """
    Add a warning message if the masquerade user would not have access to this content
    due to the content start date being in the future.
    """
    now = datetime.now(utc)
    most_future_date = course_start
    if chapter_start and section_start:
        most_future_date = max(course_start, chapter_start, section_start)
    is_masquerading = get_course_masquerade(user, course_key)
    if now < most_future_date and is_masquerading:
        group_masquerade = is_masquerading_as_student(user, course_key)
        specific_student_masquerade = is_masquerading_as_specific_student(user, course_key)
        is_staff = has_staff_roles(user, course_key)
        if group_masquerade or (specific_student_masquerade and not is_staff):
            PageLevelMessages.register_warning_message(
                request,
                HTML(_('This user does not have access to this content because \
                        the content start date is in the future')),
                once_only=True
            )
Example #16
0
def register_course_expired_message(request, course):
    """
    Add a banner notifying the user of the user course expiration date if it exists.
    """
    if not CourseDurationLimitConfig.enabled_for_enrollment(
            user=request.user, course_key=course.id):
        return

    expiration_date = get_user_course_expiration_date(request.user, course)
    if not expiration_date:
        return

    if is_masquerading_as_student(
            request.user, course.id) and timezone.now() > expiration_date:
        upgrade_message = _(
            'This learner would not have access to this course. '
            'Their access expired on {expiration_date}.')
        PageLevelMessages.register_warning_message(
            request,
            HTML(upgrade_message).format(
                expiration_date=expiration_date.strftime('%b %-d')))
    else:
        upgrade_message = _(
            'Your access to this course expires on {expiration_date}. \
                    {a_open}Upgrade now {sronly_span_open}to retain access past {expiration_date}.\
                    {span_close}{a_close}{sighted_only_span_open}for unlimited access.{span_close}'
        )
        PageLevelMessages.register_info_message(
            request,
            Text(upgrade_message).format(
                a_open=HTML('<a href="{upgrade_link}">').format(
                    upgrade_link=verified_upgrade_deadline_link(
                        user=request.user, course=course)),
                sronly_span_open=HTML('<span class="sr-only">'),
                sighted_only_span_open=HTML('<span aria-hidden="true">'),
                span_close=HTML('</span>'),
                a_close=HTML('</a>'),
                expiration_date=expiration_date.strftime('%b %-d'),
            ))
Example #17
0
def register_course_expired_message(request, course):
    """
    Add a banner notifying the user of the user course expiration date if it exists.
    """
    if not CourseDurationLimitConfig.enabled_for_enrollment(user=request.user, course_key=course.id):
        return

    expiration_date = get_user_course_expiration_date(request.user, course)
    if not expiration_date:
        return

    if is_masquerading_as_student(request.user, course.id) and timezone.now() > expiration_date:
        upgrade_message = _('This learner would not have access to this course. '
                            'Their access expired on {expiration_date}.')
        PageLevelMessages.register_warning_message(
            request,
            HTML(upgrade_message).format(
                expiration_date=expiration_date.strftime('%b %-d')
            )
        )
    else:
        upgrade_message = _('Your access to this course expires on {expiration_date}. \
                    {a_open}Upgrade now {sronly_span_open}to retain access past {expiration_date}.\
                    {span_close}{a_close}{sighted_only_span_open}for unlimited access.{span_close}')
        PageLevelMessages.register_info_message(
            request,
            Text(upgrade_message).format(
                a_open=HTML('<a href="{upgrade_link}">').format(
                    upgrade_link=verified_upgrade_deadline_link(user=request.user, course=course)
                ),
                sronly_span_open=HTML('<span class="sr-only">'),
                sighted_only_span_open=HTML('<span aria-hidden="true">'),
                span_close=HTML('</span>'),
                a_close=HTML('</a>'),
                expiration_date=expiration_date.strftime('%b %-d'),
            )
        )
Example #18
0
def register_course_expired_message(request, course):
    """
    Add a banner notifying the user of the user course expiration date if it exists.
    """
    if not CourseDurationLimitConfig.enabled_for_enrollment(
            user=request.user, course_key=course.id):
        return

    expiration_date = get_user_course_expiration_date(request.user, course)
    if not expiration_date:
        return

    if is_masquerading_as_specific_student(
            request.user, course.id) and timezone.now() > expiration_date:
        upgrade_message = _(
            'This learner does not have access to this course. '
            'Their access expired on {expiration_date}.')
        PageLevelMessages.register_warning_message(
            request,
            HTML(upgrade_message).format(expiration_date=strftime_localized(
                expiration_date, '%b. %-d, %Y')))
    else:
        enrollment = CourseEnrollment.get_enrollment(request.user, course.id)
        if enrollment is None:
            return

        upgrade_deadline = enrollment.upgrade_deadline
        now = timezone.now()
        course_upgrade_deadline = enrollment.course_upgrade_deadline
        if (not upgrade_deadline) or (upgrade_deadline < now):
            upgrade_deadline = course_upgrade_deadline

        expiration_message = _(
            '{strong_open}Audit Access Expires {expiration_date}{strong_close}'
            '{line_break}You lose all access to this course, including your progress, on '
            '{expiration_date}.')
        upgrade_deadline_message = _(
            '{line_break}Upgrade by {upgrade_deadline} to get unlimited access to the course '
            'as long as it exists on the site. {a_open}Upgrade now{sronly_span_open} to '
            'retain access past {expiration_date}{span_close}{a_close}')
        full_message = expiration_message
        if upgrade_deadline and now < upgrade_deadline:
            full_message += upgrade_deadline_message
            using_upgrade_messaging = True
        else:
            using_upgrade_messaging = False

        language = get_language()
        language_is_es = language and language.split('-')[0].lower() == 'es'
        if language_is_es:
            formatted_expiration_date = strftime_localized(
                expiration_date, '%-d de %b. de %Y').lower()
        else:
            formatted_expiration_date = strftime_localized(
                expiration_date, '%b. %-d, %Y')

        if using_upgrade_messaging:
            if language_is_es:
                formatted_upgrade_deadline = strftime_localized(
                    upgrade_deadline, '%-d de %b. de %Y').lower()
            else:
                formatted_upgrade_deadline = strftime_localized(
                    upgrade_deadline, '%b. %-d, %Y')

            PageLevelMessages.register_info_message(
                request,
                Text(full_message).format(
                    a_open=HTML('<a href="{upgrade_link}">').format(
                        upgrade_link=verified_upgrade_deadline_link(
                            user=request.user, course=course)),
                    sronly_span_open=HTML('<span class="sr-only">'),
                    span_close=HTML('</span>'),
                    a_close=HTML('</a>'),
                    expiration_date=formatted_expiration_date,
                    strong_open=HTML('<strong>'),
                    strong_close=HTML('</strong>'),
                    line_break=HTML('<br>'),
                    upgrade_deadline=formatted_upgrade_deadline))
        else:
            PageLevelMessages.register_info_message(
                request,
                Text(full_message).format(
                    span_close=HTML('</span>'),
                    expiration_date=formatted_expiration_date,
                    strong_open=HTML('<strong>'),
                    strong_close=HTML('</strong>'),
                    line_break=HTML('<br>'),
                ))