Commit 8e1e4a47 by Robert Raposa

Use markup HTML helper with Text

TNL-4160
parent 66ae31f5
......@@ -10,7 +10,7 @@ from provider.constants import CONFIDENTIAL
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin, ProgramsDataMixin
from openedx.core.djangolib.markup import escape
from openedx.core.djangolib.markup import Text
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
......@@ -64,7 +64,7 @@ class TestProgramListing(ProgramsApiConfigMixin, ProgramsDataMixin, SharedModule
self.mock_programs_api(data={'results': []})
response = self.client.get(self.studio_home)
self.assertIn(escape("You haven't created any programs yet."), response.content)
self.assertIn(Text("You haven't created any programs yet."), response.content)
# When data is provided, expect a program listing.
self.mock_programs_api()
......
## coding=utf-8
<%namespace name='static' file='static_content.html'/>
<%!
from openedx.core.djangolib.markup import ugettext as _
from django.utils.translation import ugettext as _
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
)
......
<%page expression_filter="h"/>
<%inherit file="base.html" />
<%def name="online_help_token()">
<%
......@@ -8,27 +9,30 @@ else:
%>
</%def>
<%!
from django.utils.translation import ugettext as _
from contentstore.views.helpers import xblock_studio_url, xblock_type_display_name
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
)
from openedx.core.djangolib.markup import HTML, ugettext as _
from openedx.core.djangolib.markup import Text, HTML
%>
<%block name="title">${xblock.display_name_with_default_escaped} ${xblock_type_display_name(xblock) | h}</%block>
<%block name="title">${xblock.display_name_with_default} ${xblock_type_display_name(xblock)}</%block>
<%block name="bodyclass">is-signedin course container view-container</%block>
<%namespace name='static' file='static_content.html'/>
<%block name="header_extras">
% for template_name in templates:
<script type="text/template" id="${template_name | h}-tpl">
<script type="text/template" id="${template_name}-tpl">
<%static:include path="js/${template_name}.underscore" />
</script>
% endfor
<script type="text/template" id="image-modal-tpl">
<%static:include path="common/templates/image-modal.underscore" />
</script>
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css') | h}" />
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css')}" />
</%block>
<%block name="requirejs">
......@@ -57,15 +61,15 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
ancestor_url = xblock_studio_url(ancestor)
%>
% if ancestor_url:
<a href="${ancestor_url | h}" class="navigation-item navigation-link navigation-parent">${ancestor.display_name_with_default_escaped | h}</a>
<a href="${ancestor_url}" class="navigation-item navigation-link navigation-parent">${ancestor.display_name_with_default}</a>
% else:
<span class="navigation-item navigation-parent">${ancestor.display_name_with_default_escaped | h}</span>
<span class="navigation-item navigation-parent">${ancestor.display_name_with_default}</span>
% endif
% endfor
</small>
<div class="wrapper-xblock-field incontext-editor is-editable"
data-field="display_name" data-field-display-name="${_("Display Name")}">
<h1 class="page-header-title xblock-field-value incontext-editor-value"><span class="title-value">${xblock.display_name_with_default_escaped | h}</span></h1>
<h1 class="page-header-title xblock-field-value incontext-editor-value"><span class="title-value">${xblock.display_name_with_default}</span></h1>
</div>
</div>
......@@ -74,12 +78,12 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
<ul>
% if is_unit_page:
<li class="action-item action-view nav-item">
<a href="${published_preview_link | h}" class="button button-view action-button is-disabled" aria-disabled="true" rel="external" title="${_('Open the courseware in the LMS')}">
<a href="${published_preview_link}" class="button button-view action-button is-disabled" aria-disabled="true" rel="external" title="${_('Open the courseware in the LMS')}">
<span class="action-button-text">${_("View Live Version")}</span>
</a>
</li>
<li class="action-item action-preview nav-item">
<a href="${draft_preview_link | h}" class="button button-preview action-button" rel="external" title="${_('Preview the courseware in the LMS')}">
<a href="${draft_preview_link}" class="button button-preview action-button" rel="external" title="${_('Preview the courseware in the LMS')}">
<span class="action-button-text">${_("Preview")}</span>
</a>
</li>
......@@ -102,7 +106,7 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
<article class="content-primary">
<div class="container-message wrapper-message"></div>
<section class="wrapper-xblock level-page is-hidden studio-xblock-wrapper" data-locator="${xblock_locator | h}" data-course-key="${xblock_locator.course_key | h}">
<section class="wrapper-xblock level-page is-hidden studio-xblock-wrapper" data-locator="${xblock_locator}" data-course-key="${xblock_locator.course_key}">
</section>
<div class="ui-loading">
<p><span class="spin"><i class="icon fa fa-refresh"></i></span> <span class="copy">${_("Loading")}</span></p>
......@@ -112,13 +116,13 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
% if xblock.category == 'split_test':
<div class="bit">
<h3 class="title-3">${_("Adding components")}</h3>
<p>${_("Select a component type under {strong_start}Add New Component{strong_end}. Then select a template.").format(
<p>${Text(_("Select a component type under {strong_start}Add New Component{strong_end}. Then select a template.")).format(
strong_start=HTML('<strong>'),
strong_end=HTML("</strong>"),
)}</p>
<p>${_("The new component is added at the bottom of the page or group. You can then edit and move the component.")}</p>
<h3 class="title-3">${_("Editing components")}</h3>
<p>${_("Click the {strong_start}Edit{strong_end} icon in a component to edit its content.").format(
<p>${Text(_("Click the {strong_start}Edit{strong_end} icon in a component to edit its content.")).format(
strong_start=HTML('<strong>'),
strong_end=HTML("</strong>"),
)}</p>
......@@ -129,7 +133,7 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
<p>${_("Confirm that you have properly configured content in each of your experiment groups.")}</p>
</div>
<div class="bit external-help">
<a href="${get_online_help_info(online_help_token())['doc_url'] | h}" target="_blank" class="button external-help-button">${_("Learn more about component containers")}</a>
<a href="${get_online_help_info(online_help_token())['doc_url']}" target="_blank" class="button external-help-button">${_("Learn more about component containers")}</a>
</div>
% elif is_unit_page:
<div id="publish-unit"></div>
......@@ -139,7 +143,7 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
<div class="wrapper-unit-id bar-mod-content">
<h5 class="title">${_("Location ID")}</h5>
<p class="unit-id">
<span class="unit-id-value" id="unit-location-id-input">${unit.location.name | h}</span>
<span class="unit-id-value" id="unit-location-id-input">${unit.location.name}</span>
<span class="tip"><span class="sr">Tip: </span>${_("Use this ID when you create links to this unit from other course content. You enter the ID in the URL field.")}</span>
</p>
</div>
......
<%! from openedx.core.djangolib.markup import HTML, ugettext as _ %>
<%page expression_filter="h"/>
<%!
from django.utils.translation import ugettext as _
from openedx.core.djangolib.markup import Text, HTML
%>
<%inherit file="base.html" />
......@@ -80,7 +84,7 @@
## Translators: This is an example for the name of the organization sponsoring a course, seen when filling out the form to create a new course. The organization name cannot contain spaces.
## Translators: "e.g. UniversityX or OrganizationX" is a placeholder displayed when user put no data into this field.
<input class="new-course-org" id="new-course-org" type="text" name="new-course-org" required placeholder="${_('e.g. UniversityX or OrganizationX')}" aria-describedby="tip-new-course-org tip-error-new-course-org" />
<span class="tip" id="tip-new-course-org">${_("The name of the organization sponsoring the course. {strong_start}Note: The organization name is part of the course URL.{strong_end} This cannot be changed, but you can set a different display name in Advanced Settings later.").format(
<span class="tip" id="tip-new-course-org">${Text(_("The name of the organization sponsoring the course. {strong_start}Note: The organization name is part of the course URL.{strong_end} This cannot be changed, but you can set a different display name in Advanced Settings later.")).format(
strong_start=HTML('<strong>'),
strong_end=HTML('</strong>'),
)}</span>
......@@ -93,7 +97,7 @@
## seen when filling out the form to create a new course. The number here is
## short for "Computer Science 101". It can contain letters but cannot contain spaces.
<input class="new-course-number" id="new-course-number" type="text" name="new-course-number" required placeholder="${_('e.g. CS101')}" aria-describedby="tip-new-course-number tip-error-new-course-number" />
<span class="tip" id="tip-new-course-number">${_("The unique number that identifies your course within your organization. {strong_start}Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.{strong_end}").format(
<span class="tip" id="tip-new-course-number">${Text(_("The unique number that identifies your course within your organization. {strong_start}Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.{strong_end}")).format(
strong_start=HTML('<strong>'),
strong_end=HTML('</strong>'),
)}</span>
......@@ -105,7 +109,7 @@
## Translators: This is an example for the "run" used to identify different
## instances of a course, seen when filling out the form to create a new course.
<input class="new-course-run" id="new-course-run" type="text" name="new-course-run" required placeholder="${_('e.g. 2014_T1')}" aria-describedby="tip-new-course-run tip-error-new-course-run" />
<span class="tip" id="tip-new-course-run">${_("The term in which your course will run. {strong_start}Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.{strong_end}").format(
<span class="tip" id="tip-new-course-run">${Text(_("The term in which your course will run. {strong_start}Note: This is part of your course URL, so no spaces or special characters are allowed and it cannot be changed.{strong_end}")).format(
strong_start=HTML('<strong>'),
strong_end=HTML('</strong>'),
)}</span>
......@@ -165,7 +169,7 @@
## for "Computer Science Problems". The example number may contain letters
## but must not contain spaces.
<input class="new-library-number" id="new-library-number" type="text" name="new-library-number" required placeholder="${_('e.g. CSPROB')}" aria-describedby="tip-new-library-number tip-error-new-library-number" />
<span class="tip" id="tip-new-library-number">${_("The unique code that identifies this library. {strong_start}Note: This is part of your library URL, so no spaces or special characters are allowed.{strong_end} This cannot be changed.").format(
<span class="tip" id="tip-new-library-number">${Text(_("The unique code that identifies this library. {strong_start}Note: This is part of your library URL, so no spaces or special characters are allowed.{strong_end} This cannot be changed.")).format(
strong_start=HTML('<strong>'),
strong_end=HTML('</strong>'),
)}</span>
......@@ -228,7 +232,7 @@
</div>
<div class="status-message">
<p class="copy">${_('The new course will be added to your course list in 5-10 minutes. Return to this page or {link_start}refresh it{link_end} to update the course list. The new course will need some manual configuration.').format(
<p class="copy">${Text(_('The new course will be added to your course list in 5-10 minutes. Return to this page or {link_start}refresh it{link_end} to update the course list. The new course will need some manual configuration.')).format(
link_start=HTML('<a href="#" class="action-reload">'),
link_end=HTML('</a>'),
)}</p>
......@@ -575,7 +579,7 @@
% if course_creator_status=='disallowed_for_this_site' and settings.FEATURES.get('STUDIO_REQUEST_EMAIL',''):
<div class="bit">
<h3 class="title title-3">${_("Can I create courses in {studio_name}?").format(studio_name=settings.STUDIO_NAME)}</h3>
<p>${_("In order to create courses in {studio_name}, you must {link_start}contact {platform_name} staff to help you create a course{link_end}.").format(
<p>${Text(_("In order to create courses in {studio_name}, you must {link_start}contact {platform_name} staff to help you create a course{link_end}.")).format(
studio_name=settings.STUDIO_NAME,
platform_name=settings.PLATFORM_NAME,
link_start=HTML('<a href="mailto:{email}">').format(email=settings.FEATURES.get('STUDIO_REQUEST_EMAIL','')),
......@@ -593,7 +597,7 @@
% elif course_creator_status == "denied":
<div class="bit">
<h3 class="title title-3">${_("Can I create courses in {studio_name}?").format(studio_name=settings.STUDIO_NAME)}</h3>
<p>${_("Your request to author courses in {studio_name} has been denied. Please {link_start}contact {platform_name} Staff with further questions{link_end}.").format(
<p>${Text(_("Your request to author courses in {studio_name} has been denied. Please {link_start}contact {platform_name} Staff with further questions{link_end}.")).format(
studio_name=settings.STUDIO_NAME,
platform_name=settings.PLATFORM_NAME,
link_start=HTML('<a href="mailto:{email}">').format(email=settings.TECH_SUPPORT_EMAIL),
......
<%page expression_filter="h"/>
<%inherit file="../main.html" />
<%namespace name='static' file='../static_content.html'/>
<%!
from django.utils.translation import ugettext as _
from courseware.courses import get_course_info_section, get_course_date_summary
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
from openedx.core.djangolib.markup import HTML, ugettext as _
from openedx.core.djangolib.markup import Text, HTML
%>
<%block name="pagetitle">${_("{course_number} Course Info").format(course_number=course.display_number_with_default)}</%block>
......@@ -21,7 +24,7 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
<h2 class="title">${_("You are not enrolled yet")}</h2>
<div class="copy">
<p class='enroll-message'>
${_("You are not currently enrolled in this course. {link_start}Sign up now!{link_end}").format(
${Text(_("You are not currently enrolled in this course. {link_start}Sign up now!{link_end}")).format(
link_start=HTML("<a href={}>").format(url_to_enroll),
link_end=HTML("</a>")
)}
......@@ -54,8 +57,8 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
<section class="container">
<div class="home">
<div class="page-header-main">
<h1 class="page-title">${_("Welcome to {org}'s {course_name}!").format(org=course.display_org_with_default, course_name=course.display_number_with_default) | h}</h1>
<h2 class="page-subtitle">${course.display_name_with_default | h}</h2>
<h1 class="page-title">${_("Welcome to {org}'s {course_name}!").format(org=course.display_org_with_default, course_name=course.display_number_with_default)}</h1>
<h2 class="page-subtitle">${course.display_name_with_default}</h2>
</div>
% if last_accessed_courseware_url:
<div class="page-header-secondary">
......@@ -75,25 +78,25 @@ from openedx.core.djangolib.markup import HTML, ugettext as _
% endif
<h1>${_("Course Updates and News")}</h1>
${get_course_info_section(request, masquerade_user, course, 'updates')}
${HTML(get_course_info_section(request, masquerade_user, course, 'updates'))}
</section>
<section aria-label="${_('Handout Navigation')}" class="handouts">
% if SelfPacedConfiguration.current().enable_course_home_improvements:
<h1>${_("Important Course Dates")}</h1>
${get_course_date_summary(course, user)}
${HTML(get_course_date_summary(course, user))}
% endif
<h1>${_(course.info_sidebar_name)}</h1>
${get_course_info_section(request, masquerade_user, course, 'handouts')}
${HTML(get_course_info_section(request, masquerade_user, course, 'handouts'))}
</section>
% else:
<section class="updates">
<h1>${_("Course Updates &amp; News")}</h1>
${get_course_info_section(request, masquerade_user, course, 'guest_updates')}
<h1>${_("Course Updates and News")}</h1>
${HTML(get_course_info_section(request, masquerade_user, course, 'guest_updates'))}
</section>
<section aria-label="${_('Handout Navigation')}" class="handouts">
<h1>${_("Course Handouts")}</h1>
${get_course_info_section(request, masquerade_user, course, 'guest_handouts')}
${HTML(get_course_info_section(request, masquerade_user, course, 'guest_handouts'))}
</section>
% endif
</div>
......
<%! from openedx.core.djangolib.markup import ugettext as _ %>
<%! from django.utils.translation import ugettext as _ %>
<%page expression_filter="h"/>
% for course_msg in course_enrollment_messages:
<div class="wrapper-msg urgency-high">
......
......@@ -2,48 +2,31 @@
Utilities for use in Mako markup.
"""
from django.utils.translation import ugettext as django_ugettext
from django.utils.translation import ungettext as django_ungettext
import markupsafe
# So that we can use escape() imported from here.
escape = markupsafe.escape # pylint: disable=invalid-name
# Text() can be used to declare a string as plain text, as HTML() is used
# for HTML. It simply wraps markupsafe's escape, which will HTML-escape if
# it isn't already escaped.
Text = markupsafe.escape # pylint: disable=invalid-name
def ugettext(text):
"""Translate a string, and escape it as plain text.
Use like this in Mako::
<% from openedx.core.djangolib.markup import ugettext as _ %>
<p>${_("Hello, world!")}</p>
Or with formatting::
<% from openedx.core.djangolib.markup import HTML, ugettext as _ %>
${_("Write & send {start}email{end}").format(
start=HTML("<a href='mailto:ned@edx.org'>"),
end=HTML("</a>"),
)}
def HTML(html): # pylint: disable=invalid-name
"""
return markupsafe.escape(django_ugettext(text))
Mark a string as already HTML, so that it won't be escaped before output.
def ungettext(text1, text2, num):
"""Translate a number-sensitive string, and escape it as plain text."""
return markupsafe.escape(django_ungettext(text1, text2, num))
def HTML(html): # pylint: disable=invalid-name
"""Mark a string as already HTML, so that it won't be escaped before output.
Use this function when formatting HTML into other strings. It must be
used in conjunction with ``Text()``, and both ``HTML()`` and ``Text()``
must be closed before any calls to ``format()``::
Use this when formatting HTML into other strings::
<%page expression_filter="h"/>
<%!
from django.utils.translation import ugettext as _
<% from openedx.core.djangolib.markup import HTML, ugettext as _ %>
${_("Write & send {start}email{end}").format(
start=HTML("<a href='mailto:ned@edx.org'>"),
from openedx.core.djangolib.markup import Text, HTML
%>
${Text(_("Write & send {start}email{end}")).format(
start=HTML("<a href='mailto:{}'>".format(user.email),
end=HTML("</a>"),
)}
......
......@@ -6,9 +6,10 @@ Tests for openedx.core.djangolib.markup
import unittest
import ddt
from django.utils.translation import ugettext as _, ungettext
from mako.template import Template
from openedx.core.djangolib.markup import escape, HTML, ugettext as _, ungettext
from openedx.core.djangolib.markup import Text, HTML
@ddt.ddt
......@@ -24,12 +25,12 @@ class FormatHtmlTest(unittest.TestCase):
(u"<a>нтмℓ-єѕ¢αρє∂</a>", u"&lt;a&gt;нтмℓ-єѕ¢αρє∂&lt;/a&gt;"),
)
def test_simple(self, (before, after)):
self.assertEqual(unicode(_(before)), after) # pylint: disable=translation-of-non-string
self.assertEqual(unicode(escape(before)), after)
self.assertEqual(unicode(Text(_(before))), after) # pylint: disable=translation-of-non-string
self.assertEqual(unicode(Text(before)), after)
def test_formatting(self):
# The whole point of this function is to make sure this works:
out = _(u"Point & click {start}here{end}!").format(
out = Text(_(u"Point & click {start}here{end}!")).format(
start=HTML("<a href='http://edx.org'>"),
end=HTML("</a>"),
)
......@@ -41,7 +42,7 @@ class FormatHtmlTest(unittest.TestCase):
def test_nested_formatting(self):
# Sometimes, you have plain text, with html inserted, and the html has
# plain text inserted. It gets twisty...
out = _(u"Send {start}email{end}").format(
out = Text(_(u"Send {start}email{end}")).format(
start=HTML("<a href='mailto:{email}'>").format(email="A&B"),
end=HTML("</a>"),
)
......@@ -54,8 +55,12 @@ class FormatHtmlTest(unittest.TestCase):
# The default_filters used here have to match the ones in edxmako.
template = Template(
"""
<%! from openedx.core.djangolib.markup import HTML, ugettext as _ %>
${_(u"A & {BC}").format(BC=HTML("B & C"))}
<%!
from django.utils.translation import ugettext as _
from openedx.core.djangolib.markup import Text, HTML
%>
${Text(_(u"A & {BC}")).format(BC=HTML("B & C"))}
""",
default_filters=['decode.utf8', 'h'],
)
......@@ -64,5 +69,5 @@ class FormatHtmlTest(unittest.TestCase):
def test_ungettext(self):
for i in [1, 2]:
out = ungettext("1 & {}", "2 & {}", i).format(HTML("<>"))
out = Text(ungettext("1 & {}", "2 & {}", i)).format(HTML("<>"))
self.assertEqual(out, "{} &amp; <>".format(i))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment