Commit 1122c40f by cahrens

Moving ignore rules to relevant classes instead general rule.

Also replaces section with div, updates short_id, and makes textbox accessible.

TNL-5576, TNL-5575, TNL-5574, TNL-5671, TNL-5376
parent c3b6b104
...@@ -46,6 +46,7 @@ ACCESSIBLE_CAPA_INPUT_TYPES = [ ...@@ -46,6 +46,7 @@ ACCESSIBLE_CAPA_INPUT_TYPES = [
'optioninput', 'optioninput',
'textline', 'textline',
'formulaequationinput', 'formulaequationinput',
'textbox',
] ]
# these get captured as student responses # these get captured as student responses
......
...@@ -818,8 +818,17 @@ class CodeInput(InputTypeBase): ...@@ -818,8 +818,17 @@ class CodeInput(InputTypeBase):
self.setup_code_response_rendering() self.setup_code_response_rendering()
def _extra_context(self): def _extra_context(self):
"""Defined queue_len, add it """ """
return {'queue_len': self.queue_len, } Define queue_len, arial_label and code mirror exit message context variables
"""
_ = self.capa_system.i18n.ugettext
return {
'queue_len': self.queue_len,
'aria_label': _('{programming_language} editor').format(
programming_language=self.loaded_attributes.get('mode')
),
'code_mirror_exit_message': _('Press ESC then TAB or click outside of the code editor to exit')
}
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
......
...@@ -253,23 +253,26 @@ class LoncapaResponse(object): ...@@ -253,23 +253,26 @@ class LoncapaResponse(object):
""" """
_ = self.capa_system.i18n.ugettext _ = self.capa_system.i18n.ugettext
# get responsetype index to make responsetype label # response_id = problem_id + response index
response_index = self.xml.attrib['id'].split('_')[-1] response_id = self.xml.attrib['id']
response_index = response_id.split('_')[-1]
# Translators: index here could be 1,2,3 and so on # Translators: index here could be 1,2,3 and so on
response_label = _(u'Question {index}').format(index=response_index) response_label = _(u'Question {index}').format(index=response_index)
# wrap the content inside a section # wrap the content inside a section
tree = etree.Element('section') tree = etree.Element('div')
tree.set('class', 'wrapper-problem-response') tree.set('class', 'wrapper-problem-response')
tree.set('tabindex', '-1') tree.set('tabindex', '-1')
tree.set('aria-label', response_label) tree.set('aria-label', response_label)
tree.set('role', 'group')
if self.xml.get('multiple_inputtypes'): if self.xml.get('multiple_inputtypes'):
# add <div> to wrap all inputtypes # add <div> to wrap all inputtypes
content = etree.SubElement(tree, 'div') content = etree.SubElement(tree, 'div')
content.set('class', 'multi-inputs-group') content.set('class', 'multi-inputs-group')
content.set('role', 'group') content.set('role', 'group')
content.set('aria-labelledby', self.xml.get('id')) content.set('aria-labelledby', response_id)
else: else:
content = tree content = tree
......
<%page expression_filter="h"/>
<%! <%!
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from openedx.core.djangolib.markup import HTML from openedx.core.djangolib.markup import HTML
%> %>
<section id="textbox_${id}" class="capa_inputtype textbox cminput"> <div id="textbox_${id}" class="capa_inputtype textbox cminput">
<textarea rows="${rows}" cols="${cols}" name="input_${id}" % if response_data['label']:
aria-label="${_("{programming_language} editor").format(programming_language=mode)}" <label class="problem-group-label" for="cm-textarea-${id}">${response_data['label']}</label>
aria-describedby="answer_${id}" % endif
id="input_${id}" <textarea rows="${rows}" cols="${cols}" name="input_${id}"
aria-label="${aria_label}"
aria-describedby="answer_${id}"
id="input_${id}"
tabindex="0" tabindex="0"
data-mode="${mode}" data-mode="${mode}"
data-tabsize="${tabsize}" data-tabsize="${tabsize}"
...@@ -16,7 +20,10 @@ from openedx.core.djangolib.markup import HTML ...@@ -16,7 +20,10 @@ from openedx.core.djangolib.markup import HTML
% if hidden: % if hidden:
style="display:none;" style="display:none;"
% endif % endif
>${value|h}</textarea> >${value}</textarea>
<span class="cm-editor-exit-message capa-message" id="cm-editor-exit-message-${id}">
${code_mirror_exit_message}
</span>
<div class="grader-status" tabindex="-1"> <div class="grader-status" tabindex="-1">
<span id="status_${id}" <span id="status_${id}"
...@@ -41,4 +48,4 @@ from openedx.core.djangolib.markup import HTML ...@@ -41,4 +48,4 @@ from openedx.core.djangolib.markup import HTML
<div class="external-grader-message"> <div class="external-grader-message">
${HTML(msg)} ${HTML(msg)}
</div> </div>
</section> </div>
...@@ -90,10 +90,10 @@ def mock_capa_module(): ...@@ -90,10 +90,10 @@ def mock_capa_module():
return capa_module return capa_module
def new_loncapa_problem(xml, capa_system=None, seed=723, use_capa_render_template=False): def new_loncapa_problem(xml, problem_id='1', capa_system=None, seed=723, use_capa_render_template=False):
"""Construct a `LoncapaProblem` suitable for unit tests.""" """Construct a `LoncapaProblem` suitable for unit tests."""
render_template = capa_render_template if use_capa_render_template else None render_template = capa_render_template if use_capa_render_template else None
return LoncapaProblem(xml, id='1', seed=seed, capa_system=capa_system or test_capa_system(render_template), return LoncapaProblem(xml, id=problem_id, seed=seed, capa_system=capa_system or test_capa_system(render_template),
capa_module=mock_capa_module()) capa_module=mock_capa_module())
......
...@@ -473,7 +473,7 @@ class CAPAMultiInputProblemTest(unittest.TestCase): ...@@ -473,7 +473,7 @@ class CAPAMultiInputProblemTest(unittest.TestCase):
# verify that only one multi input group div is present at correct path # verify that only one multi input group div is present at correct path
multi_inputs_group = html.xpath( multi_inputs_group = html.xpath(
'//section[@class="wrapper-problem-response"]/div[@class="multi-inputs-group"]' '//div[@class="wrapper-problem-response"]/div[@class="multi-inputs-group"]'
) )
self.assertEqual(len(multi_inputs_group), 1) self.assertEqual(len(multi_inputs_group), 1)
......
"""
CAPA HTML rendering tests.
"""
import ddt
import unittest import unittest
from lxml import etree from lxml import etree
import os import os
...@@ -9,7 +13,11 @@ from .response_xml_factory import StringResponseXMLFactory, CustomResponseXMLFac ...@@ -9,7 +13,11 @@ from .response_xml_factory import StringResponseXMLFactory, CustomResponseXMLFac
from capa.tests.helpers import test_capa_system, new_loncapa_problem from capa.tests.helpers import test_capa_system, new_loncapa_problem
@ddt.ddt
class CapaHtmlRenderTest(unittest.TestCase): class CapaHtmlRenderTest(unittest.TestCase):
"""
CAPA HTML rendering tests class.
"""
def setUp(self): def setUp(self):
super(CapaHtmlRenderTest, self).setUp() super(CapaHtmlRenderTest, self).setUp()
...@@ -142,28 +150,28 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -142,28 +150,28 @@ class CapaHtmlRenderTest(unittest.TestCase):
# Mock out the template renderer # Mock out the template renderer
the_system = test_capa_system() the_system = test_capa_system()
the_system.render_template = mock.Mock() the_system.render_template = mock.Mock()
the_system.render_template.return_value = "<div>Input Template Render</div>" the_system.render_template.return_value = "<div class='input-template-render'>Input Template Render</div>"
# Create the problem and render the HTML # Create the problem and render the HTML
problem = new_loncapa_problem(xml_str, capa_system=the_system) problem = new_loncapa_problem(xml_str, capa_system=the_system)
rendered_html = etree.XML(problem.get_html()) rendered_html = etree.XML(problem.get_html())
# Expect problem has been turned into a <div> # Expect problem has been turned into a <div>
self.assertEqual(rendered_html.tag, "div") self.assertEqual(rendered_html.tag, "div")
# Expect that the response has been turned into a <section> with correct attributes # Expect that the response has been turned into a <div> with correct attributes
response_element = rendered_html.find("section") response_element = rendered_html.find('div')
self.assertEqual(response_element.tag, "section")
self.assertEqual(response_element.tag, "div")
self.assertEqual(response_element.attrib["aria-label"], "Question 1") self.assertEqual(response_element.attrib["aria-label"], "Question 1")
# Expect that the response <section> # Expect that the response div.wrapper-problem-response
# that contains a <div> for the textline # that contains a <div> for the textline
textline_element = response_element.find("div") textline_element = response_element.find('div')
self.assertEqual(textline_element.text, 'Input Template Render') self.assertEqual(textline_element.text, 'Input Template Render')
# Expect a child <div> for the solution # Expect a child <div> for the solution
# with the rendered template # with the rendered template
solution_element = rendered_html.find("div") solution_element = rendered_html.xpath('//div[@class="input-template-render"]')[0]
self.assertEqual(solution_element.text, 'Input Template Render') self.assertEqual(solution_element.text, 'Input Template Render')
# Expect that the template renderer was called with the correct # Expect that the template renderer was called with the correct
...@@ -218,9 +226,9 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -218,9 +226,9 @@ class CapaHtmlRenderTest(unittest.TestCase):
""" """
problem = new_loncapa_problem(xml) problem = new_loncapa_problem(xml)
rendered_html = etree.XML(problem.get_html()) rendered_html = etree.XML(problem.get_html())
sections = rendered_html.findall('section') response_elements = rendered_html.findall('div')
self.assertEqual(sections[0].attrib['aria-label'], 'Question 1') self.assertEqual(response_elements[0].attrib['aria-label'], 'Question 1')
self.assertEqual(sections[1].attrib['aria-label'], 'Question 2') self.assertEqual(response_elements[1].attrib['aria-label'], 'Question 2')
def test_render_response_with_overall_msg(self): def test_render_response_with_overall_msg(self):
# CustomResponse script that sets an overall_message # CustomResponse script that sets an overall_message
......
...@@ -1181,3 +1181,43 @@ class SchematicInputTemplateTest(TemplateTestCase): ...@@ -1181,3 +1181,43 @@ class SchematicInputTemplateTest(TemplateTestCase):
Verify aria-label attribute rendering. Verify aria-label attribute rendering.
""" """
self.assert_label(aria_label=True) self.assert_label(aria_label=True)
class CodeinputTemplateTest(TemplateTestCase):
"""
Test mako template for `<textbox>` input
"""
TEMPLATE_NAME = 'codeinput.html'
def setUp(self):
super(CodeinputTemplateTest, self).setUp()
self.context = {
'id': '1',
'status': Status('correct'),
'mode': 'parrot',
'linenumbers': 'false',
'rows': '37',
'cols': '11',
'tabsize': '7',
'hidden': '',
'msg': '',
'value': 'print "good evening"',
'aria_label': 'python editor',
'code_mirror_exit_message': 'Press ESC then TAB or click outside of the code editor to exit',
'response_data': self.RESPONSE_DATA,
'describedby': self.DESCRIBEDBY,
}
def test_label(self):
"""
Verify question label is rendered correctly.
"""
self.assert_label(xpath="//label[@class='problem-group-label']")
def test_editor_exit_message(self):
"""
Verify that editor exit message is rendered.
"""
xml = self.render_to_xml(self.context)
self.assert_has_text(xml, '//span[@id="cm-editor-exit-message-1"]', self.context['code_mirror_exit_message'])
...@@ -421,6 +421,8 @@ class CodeInputTest(unittest.TestCase): ...@@ -421,6 +421,8 @@ class CodeInputTest(unittest.TestCase):
'hidden': '', 'hidden': '',
'tabsize': int(tabsize), 'tabsize': int(tabsize),
'queue_len': '3', 'queue_len': '3',
'aria_label': '{mode} editor'.format(mode=mode),
'code_mirror_exit_message': 'Press ESC then TAB or click outside of the code editor to exit',
'response_data': RESPONSE_DATA, 'response_data': RESPONSE_DATA,
'describedby_html': DESCRIBEDBY 'describedby_html': DESCRIBEDBY
} }
......
...@@ -908,6 +908,12 @@ div.problem { ...@@ -908,6 +908,12 @@ div.problem {
} }
} }
.capa-message {
display: inline-block;
color: $gray-d1;
-webkit-font-smoothing: antialiased;
}
// +Problem - Actions // +Problem - Actions
// ==================== // ====================
div.problem .action { div.problem .action {
......
<div id="textbox_101" class="capa_inputtype textbox cminput">
<label class="problem-group-label" for="cm-textarea-101">question label here</label>
<textarea rows="40" cols="80" name="input_101"
aria-label="python editor"
aria-describedby="answer_101"
id="input_101"
tabindex="0"
data-mode="python"
data-tabsize="4"
data-linenums="true"
>write some awesome code</textarea>
<span class="cm-editor-exit-message capa-message" id="cm-editor-exit-message-101">
Press ESC then TAB or click outside of the code editor to exit
</span>
<div class="grader-status" tabindex="-1">
<span id="status_101" class="correct" aria-describedby="input_101">
<span class="status sr">correct</span>
</span>
</div>
</div>
...@@ -834,3 +834,25 @@ describe 'Problem', -> ...@@ -834,3 +834,25 @@ describe 'Problem', ->
expect(@problem.poll.calls.count()).toEqual(6) expect(@problem.poll.calls.count()).toEqual(6)
expect($('.notification-gentle-alert .notification-message').text()).toEqual("The grading process is still running. Refresh the page to see updates.") expect($('.notification-gentle-alert .notification-message').text()).toEqual("The grading process is still running. Refresh the page to see updates.")
describe 'codeinput problem', ->
codeinputProblemHtml = readFixtures('codeinput_problem.html')
beforeEach ->
spyOn($, 'postWithPrefix').and.callFake (url, callback) ->
callback html: codeinputProblemHtml
@problem = new Problem($('.xblock-student_view'))
@problem.render(codeinputProblemHtml)
it 'has rendered with correct a11y info', ->
CodeMirrorTextArea = $('textarea')[1]
CodeMirrorTextAreaId = 'cm-textarea-101'
# verify that question label has correct `for` attribute value
expect($('.problem-group-label').attr('for')).toEqual(CodeMirrorTextAreaId)
# verify that codemirror textarea has correct `id` attribute value
expect($(CodeMirrorTextArea).attr('id')).toEqual(CodeMirrorTextAreaId)
# verify that codemirror textarea has correct `aria-describedby` attribute value
expect($(CodeMirrorTextArea).attr('aria-describedby')).toEqual('cm-editor-exit-message-101 status_101')
...@@ -397,7 +397,7 @@ class @Problem ...@@ -397,7 +397,7 @@ class @Problem
labeled_status.push($(element).text()) labeled_status.push($(element).text())
return labeled_status return labeled_status
reset: => reset: =>
@disableAllButtonsWhileRunning @reset_internal, false @disableAllButtonsWhileRunning @reset_internal, false
...@@ -671,7 +671,7 @@ class @Problem ...@@ -671,7 +671,7 @@ class @Problem
mode = element.data("mode") mode = element.data("mode")
linenumbers = element.data("linenums") linenumbers = element.data("linenums")
spaces = Array(parseInt(tabsize) + 1).join(" ") spaces = Array(parseInt(tabsize) + 1).join(" ")
CodeMirror.fromTextArea element[0], { CodeMirrorEditor = CodeMirror.fromTextArea element[0], {
lineNumbers: linenumbers lineNumbers: linenumbers
indentUnit: tabsize indentUnit: tabsize
tabSize: tabsize tabSize: tabsize
...@@ -688,7 +688,12 @@ class @Problem ...@@ -688,7 +688,12 @@ class @Problem
cm.replaceSelection(spaces, "end") cm.replaceSelection(spaces, "end")
return false return false
} }
} }
id = element.attr("id").replace(/^input_/, "")
CodeMirrorTextArea = CodeMirrorEditor.getInputField()
CodeMirrorTextArea.setAttribute("id", "cm-textarea-#{id}")
CodeMirrorTextArea.setAttribute("aria-describedby", "cm-editor-exit-message-#{id} status_#{id}")
return CodeMirrorEditor
inputtypeShowAnswerMethods: inputtypeShowAnswerMethods:
choicegroup: (element, display, answers) => choicegroup: (element, display, answers) =>
......
...@@ -79,7 +79,7 @@ class StaffDebugPage(PageObject): ...@@ -79,7 +79,7 @@ class StaffDebugPage(PageObject):
url = None url = None
def is_browser_on_page(self): def is_browser_on_page(self):
return self.q(css='section.staff-modal').present return self.q(css='.staff-modal').present
def reset_attempts(self, user=None): def reset_attempts(self, user=None):
""" """
......
...@@ -658,7 +658,7 @@ class CAPAProblemA11yBaseTestMixin(object): ...@@ -658,7 +658,7 @@ class CAPAProblemA11yBaseTestMixin(object):
# Set the scope to the problem question # Set the scope to the problem question
problem_page.a11y_audit.config.set_scope( problem_page.a11y_audit.config.set_scope(
include=['section.wrapper-problem-response'] include=['.wrapper-problem-response']
) )
# Run the accessibility audit. # Run the accessibility audit.
......
...@@ -371,15 +371,6 @@ class ProblemTypeTestMixin(object): ...@@ -371,15 +371,6 @@ class ProblemTypeTestMixin(object):
self.problem_page.a11y_audit.config.set_scope( self.problem_page.a11y_audit.config.set_scope(
include=['div#seq_content']) include=['div#seq_content'])
self.problem_page.a11y_audit.config.set_rules({
"ignore": [
'checkboxgroup', # TODO: AC-491
'radiogroup', # TODO: AC-491
'section', # TODO: AC-491
'label', # TODO: AC-491
]
})
# Run the accessibility audit. # Run the accessibility audit.
self.problem_page.a11y_audit.check_for_accessibility_errors() self.problem_page.a11y_audit.check_for_accessibility_errors()
...@@ -422,6 +413,12 @@ class AnnotationProblemTypeTest(ProblemTypeTestBase, ProblemTypeTestMixin): ...@@ -422,6 +413,12 @@ class AnnotationProblemTypeTest(ProblemTypeTestBase, ProblemTypeTestMixin):
""" """
super(AnnotationProblemTypeTest, self).setUp(*args, **kwargs) super(AnnotationProblemTypeTest, self).setUp(*args, **kwargs)
self.problem_page.a11y_audit.config.set_rules({
"ignore": [
'label', # TODO: AC-491
]
})
def answer_problem(self, correctness): def answer_problem(self, correctness):
""" """
Answer annotation problem. Answer annotation problem.
...@@ -939,6 +936,14 @@ class RadioTextProblemTypeTest(ChoiceTextProbelmTypeTestBase, ProblemTypeTestMix ...@@ -939,6 +936,14 @@ class RadioTextProblemTypeTest(ChoiceTextProbelmTypeTestBase, ProblemTypeTestMix
""" """
super(RadioTextProblemTypeTest, self).setUp(*args, **kwargs) super(RadioTextProblemTypeTest, self).setUp(*args, **kwargs)
self.problem_page.a11y_audit.config.set_rules({
"ignore": [
'radiogroup', # TODO: AC-491
'label', # TODO: AC-491
'section', # TODO: AC-491
]
})
class CheckboxTextProblemTypeTest(ChoiceTextProbelmTypeTestBase, ProblemTypeTestMixin): class CheckboxTextProblemTypeTest(ChoiceTextProbelmTypeTestBase, ProblemTypeTestMixin):
""" """
...@@ -966,6 +971,14 @@ class CheckboxTextProblemTypeTest(ChoiceTextProbelmTypeTestBase, ProblemTypeTest ...@@ -966,6 +971,14 @@ class CheckboxTextProblemTypeTest(ChoiceTextProbelmTypeTestBase, ProblemTypeTest
""" """
super(CheckboxTextProblemTypeTest, self).setUp(*args, **kwargs) super(CheckboxTextProblemTypeTest, self).setUp(*args, **kwargs)
self.problem_page.a11y_audit.config.set_rules({
"ignore": [
'checkboxgroup', # TODO: AC-491
'label', # TODO: AC-491
'section', # TODO: AC-491
]
})
class ImageProblemTypeTest(ProblemTypeTestBase, ProblemTypeTestMixin): class ImageProblemTypeTest(ProblemTypeTestBase, ProblemTypeTestMixin):
""" """
......
...@@ -560,7 +560,7 @@ html.video-fullscreen { ...@@ -560,7 +560,7 @@ html.video-fullscreen {
} }
} }
section.xqa-modal, section.staff-modal, section.history-modal { .xqa-modal, .staff-modal, .history-modal {
width: 80%; width: 80%;
height: 80%; height: 80%;
left: left(20%); left: left(20%);
......
<div id="problem_${element_id}" class="problems-wrapper" data-problem-id="${id}" data-url="${ajax_url}" data-progress_status="${progress_status}" data-progress_detail="${progress_detail}" data-content="${content | h}" data-graded="${graded}"></div> <div id="problem_${element_id}" class="problems-wrapper" role="group" aria-labelledby="${element_id}-problem-title" data-problem-id="${id}" data-url="${ajax_url}" data-progress_status="${progress_status}" data-progress_detail="${progress_detail}" data-content="${content | h}" data-graded="${graded}"></div>
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
${'' if notification_name == 'submit' else 'is-hidden' }" ${'' if notification_name == 'submit' else 'is-hidden' }"
tabindex="-1"> tabindex="-1">
<span class="icon fa ${notification_icon}" aria-hidden="true"></span> <span class="icon fa ${notification_icon}" aria-hidden="true"></span>
<span class="notification-message" aria-describedby="${ id }-problem-title">${notification_message} <span class="notification-message" aria-describedby="${ short_id }-problem-title">${notification_message}
</span> </span>
<div class="notification-btn-wrapper"> <div class="notification-btn-wrapper">
% if notification_name is 'hint': % if notification_name is 'hint':
......
...@@ -31,7 +31,7 @@ ${block_content} ...@@ -31,7 +31,7 @@ ${block_content}
</div> </div>
% endif % endif
<section aria-hidden="true" role="dialog" tabindex="-1" id="${element_id}_xqa-modal" class="modal xqa-modal"> <div aria-hidden="true" role="dialog" tabindex="-1" id="${element_id}_xqa-modal" class="modal xqa-modal">
<div class="inner-wrapper"> <div class="inner-wrapper">
<header> <header>
<h2>${_("{platform_name} Content Quality Assessment").format(platform_name=settings.PLATFORM_NAME)}</h2> <h2>${_("{platform_name} Content Quality Assessment").format(platform_name=settings.PLATFORM_NAME)}</h2>
...@@ -51,9 +51,9 @@ ${block_content} ...@@ -51,9 +51,9 @@ ${block_content}
</form> </form>
</div> </div>
</section> </div>
<section aria-hidden="true" role="dialog" tabindex="-1" class="modal staff-modal" id="${element_id}_debug" > <div aria-hidden="true" role="dialog" tabindex="-1" class="modal staff-modal" id="${element_id}_debug" >
<div class="inner-wrapper"> <div class="inner-wrapper">
<header> <header>
<h2>${_('Staff Debug')}</h2> <h2>${_('Staff Debug')}</h2>
...@@ -106,9 +106,9 @@ category = ${category | h} ...@@ -106,9 +106,9 @@ category = ${category | h}
<div id="histogram_${element_id}" class="histogram" data-histogram="${histogram}"></div> <div id="histogram_${element_id}" class="histogram" data-histogram="${histogram}"></div>
%endif %endif
</div> </div>
</section> </div>
<section aria-hidden="true" role="dialog" tabindex="-1" class="modal history-modal" id="${element_id}_history"> <div aria-hidden="true" role="dialog" tabindex="-1" class="modal history-modal" id="${element_id}_history">
<div class="inner-wrapper"> <div class="inner-wrapper">
<header> <header>
<h2>${_("Submission History Viewer")}</h2> <h2>${_("Submission History Viewer")}</h2>
...@@ -125,7 +125,7 @@ category = ${category | h} ...@@ -125,7 +125,7 @@ category = ${category | h}
<div id="${element_id}_history_text" class="staff_info" style="display:block"> <div id="${element_id}_history_text" class="staff_info" style="display:block">
</div> </div>
</div> </div>
</section> </div>
<div id="${element_id}_setup"></div> <div id="${element_id}_setup"></div>
......
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