Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactors Journalist Interface for semantic HTML5/ARIA markup #6165

Merged
merged 46 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
054a35e
first pass through "/login"
cfm Nov 3, 2021
9435185
replicates changes from Source Interface's locale selector
cfm Nov 3, 2021
aeecefe
first pass through "/"
cfm Nov 4, 2021
c47652c
first pass through "/": refactors source listing as TABLE
cfm Nov 4, 2021
ed47444
first pass through "/col/<filesystem_id>": refactors p.breadcrumbs as UL
cfm Nov 5, 2021
fe30666
first pass through "/col/<filesystem_id>": refactors collection listi…
cfm Nov 5, 2021
c465854
first pass through "/col/<filesystem_id>"
cfm Nov 5, 2021
5f6ca09
first pass through "/account/account"
cfm Nov 5, 2021
c50355e
first pass through "/admin"
cfm Nov 5, 2021
fc02271
first pass through "/admin/add"
cfm Nov 6, 2021
d22c9f8
first pass through "*modal.html": refactors as DIALOGs
cfm Nov 8, 2021
d67ee1a
first pass through "/admin/config"
cfm Nov 8, 2021
b06602c
first pass through HOTP/TOTP routes
cfm Nov 8, 2021
2a05ef7
first pass through "/bulk"
cfm Nov 9, 2021
14d3bdc
second pass through "/login": DIV#js-strings must be in BODY, not HEAD
cfm Nov 9, 2021
e87dd85
second pass through "/"
cfm Nov 9, 2021
0d6112e
second pass through "/col/<filesystem_id>"
cfm Nov 9, 2021
e1d3eec
second pass through "/account/account"
cfm Nov 9, 2021
ca7128e
second pass through "/account/account": refactors CSS tooltips to loo…
cfm Nov 9, 2021
a9d5d2f
second pass through "/admin"
cfm Nov 9, 2021
47d62a0
test /admin
cfm Nov 10, 2021
f8e9cb1
second pass through "/admin/config"
cfm Nov 10, 2021
04a2bd3
adds missing ID attributes for LABEL[for]
cfm Dec 13, 2021
dd0e700
expands ARIA-LABELs to add context to recurring UI elements
cfm Dec 13, 2021
dac0737
refactors unnecessary FORM/SECTIONs to standalone A.btn elements
cfm Dec 13, 2021
3f12bea
removes unnecessary DIVs
cfm Dec 13, 2021
02e0603
avoids unnecessarily introducing a new source string
cfm Dec 14, 2021
b4962c4
third pass through "/login"
cfm Dec 14, 2021
4927aab
third pass through "/": fixes "index.html" and "_source_row.html" sty…
cfm Dec 15, 2021
1a5aff1
third pass through "/": fixes "_sources_confirmation*_modal.html" sty…
cfm Dec 16, 2021
cb92c13
third pass through "/col/<filesystem_id>": fixes "col.html" styling
cfm Dec 16, 2021
4f032db
third pass through "/col/<filesystem_id>": fixes tests
cfm Dec 21, 2021
d48f188
third pass through "/col/<filesystem_id>": fixes "_confirmation_modal…
cfm Dec 16, 2021
9d8b7a8
third pass through "/account/account": fixes "edit_account.html" styling
cfm Dec 20, 2021
4e66411
third pass through "/admin": fixes "admin.html" styling
cfm Dec 21, 2021
035d9fe
third pass through "/admin": fixes tests
cfm Dec 21, 2021
5a3983a
third pass through "/admin/config": fixes "config.html" styling
cfm Dec 21, 2021
eb33484
third pass through "/admin/add": fixes "admin_add_user.html" styling
cfm Dec 21, 2021
840f6d9
third pass through "/{account,admin}/2fa": fixes "{account,admin}_new…
cfm Dec 21, 2021
d86cd4c
third pass through "/{account,admin}/reset-2fa-secret": fixes "{accou…
cfm Dec 21, 2021
7f8e596
fixes "[preferences_saved_]flashed.html" styling
cfm Dec 21, 2021
768d71f
fixes regressed NAV placement
cfm Jan 4, 2022
ba01d57
corrects placement of elements that accompany FORM INPUTs
cfm Jan 4, 2022
f371034
prevents wrapping of THs in collections, submissions, and users TABLEs
cfm Jan 4, 2022
f1f960c
groups related fields in a FIELDSET with an explanatory LEGEND (per r…
cfm Jan 19, 2022
262ce5a
formats templates for more-consistent indentation
cfm Jan 19, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions securedrop/journalist_app/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,13 @@ class NewUserForm(FlaskForm):
username = StringField('username', validators=[
InputRequired(message=gettext('This field is required.')),
minimum_length_validation, check_invalid_usernames
])
first_name = StringField('first_name', validators=[name_length_validation, Optional()])
last_name = StringField('last_name', validators=[name_length_validation, Optional()])
],
render_kw={'aria-describedby': 'username-notes'},
)
first_name = StringField('first_name', validators=[name_length_validation, Optional()],
render_kw={'aria-describedby': 'name-notes'})
last_name = StringField('last_name', validators=[name_length_validation, Optional()],
render_kw={'aria-describedby': 'name-notes'})
password = HiddenField('password')
is_admin = BooleanField('is_admin')
is_hotp = BooleanField('is_hotp')
Expand Down
29 changes: 16 additions & 13 deletions securedrop/journalist_templates/_confirmation_modal.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
<div id="{{ modal_data.modal_id }}" class="modal-dialog">
<a href="#close" class="external"></a>
<div>
<a href="#close" title="{{ gettext('Close') }}" class="close"> <img src="{{ url_for('static', filename='i/modal-x-white.png') }}" alt="{{ gettext('Close') }}" height="24" width="24"></a>
<h2>{{ modal_data.modal_header }}</h2>
<p>{{ modal_data.modal_body }}</p>
<a href="#close" class="external" aria-label="{{ gettext('Close') }}"></a>
<dialog open aria-labelledby="{{ modal_data.modal_id }}-heading">
<a href="#close" title="{{ gettext('Close') }}" class="close">
<span>{{ gettext('Close') }}</span>
</a>
<h2 id="{{ modal_data.modal_id }}-heading">{{ modal_data.modal_header }}</h2>
{{ modal_data.modal_body }}
{% if modal_data.modal_warning is defined %}
<p><em>{{ modal_data.modal_warning }}</em></p>
<p class="warning">{{ modal_data.modal_warning }}</p>
{% endif %}
<center>
<div class="btn-row">
<a href="#close" id="{{ modal_data.cancel_id }}" title="{{ gettext('Cancel') }}" class="btn cancel small">{{ gettext('Cancel') }}</a>
<button type="submit" id="{{ modal_data.submit_id }}" name="action" value="delete" {% if modal_data.modal_action is defined %} formaction="{{ modal_data.modal_action }}" {% endif %} class="btn small {{ modal_data.submit_btn_type }}">{{ modal_data.submit_btn_text }}</button>
</div>
</center>
</div>
<div class="btn-row">
<a href="#close" id="{{ modal_data.cancel_id }}" title="{{ gettext('Cancel') }}"
class="btn cancel small">{{ gettext('Cancel') }}</a>
<button type="submit" id="{{ modal_data.submit_id }}" name="action" value="delete"
{% if modal_data.modal_action is defined %} formaction="{{ modal_data.modal_action }}" {% endif %}
class="btn small {{ modal_data.submit_btn_type }}">{{ modal_data.submit_btn_text }}</button>
</div>
</dialog>
</div>
85 changes: 48 additions & 37 deletions securedrop/journalist_templates/_source_row.html
Original file line number Diff line number Diff line change
@@ -1,44 +1,55 @@
{% set docs = source.documents_messages_count()['documents'] %}
{% set msgs = source.documents_messages_count()['messages'] %}
<li class="source {% if source.num_unread != 0 %}unread{% else %}read{% endif %}" data-source-designation="{{ source.journalist_designation|lower }}">
<time class="date" title="{{ source.last_updated|rel_datetime_format }}" datetime="{{ source.last_updated|html_datetime_format }}">{{ source.last_updated|rel_datetime_format(relative=True) }}</time>
<div class="designation">
<tr class="source {% if source.num_unread != 0 %}unread{% else %}read{% endif %}"
data-source-designation="{{ source.journalist_designation|lower }}">
<th class="designation" scope="row">
{% if source.star.starred %}
<button class="button-star starred" type="submit" formaction="{{ url_for('col.remove_star', filesystem_id=source.filesystem_id) }}" aria-label="{{ gettext('Un-star {designation}').format(designation=source.journalist_designation) }}">
<img src="{{ url_for('static', filename='icons/starred.png') }}" class="icon-drop icon-star-source" width="16" height="15" alt="{{ gettext('{designation} is starred').format(designation=source.journalist_designation) }}">
</button>
<input type="checkbox" name="cols_selected" value="{{ source.filesystem_id }}" aria-labelledby="starred-source-link-{{ loop_index }}">
<a href="/col/{{ source.filesystem_id }}" id="starred-source-link-{{ loop_index }}" class="code-name {% if source.num_unread != 0 %}unread{% else %}read{% endif %}">
{{ source.journalist_designation }}
</a>
<button class="button-star starred" type="submit"
formaction="{{ url_for('col.remove_star', filesystem_id=source.filesystem_id) }}"
aria-label="{{ gettext('Un-star {designation}').format(designation=source.journalist_designation) }}">
<span>Unstar</span>
</button>
<input type="checkbox" name="cols_selected" value="{{ source.filesystem_id }}"
aria-labelledby="starred-source-link-{{ loop_index }}">
<a href="/col/{{ source.filesystem_id }}" id="starred-source-link-{{ loop_index }}"
class="code-name {% if source.num_unread != 0 %}unread{% else %}read{% endif %}">
{{ source.journalist_designation }}
</a>
{% else %}
<button class="button-star un-starred" type="submit" formaction="{{ url_for('col.add_star', filesystem_id=source.filesystem_id) }}" aria-label="{{ gettext('Star {designation}' ).format(designation=source.journalist_designation) }}">
<img src="{{ url_for('static', filename='icons/unstarred.png') }}" class="icon-drop icon-star-source off-hover" width="16" height="15" alt="{{ gettext('{designation} is not starred').format(designation=source.journalist_designation) }}">
<img src="{{ url_for('static', filename='icons/starred.png') }}" class="icon-drop icon-star-source on-hover" width="16" height="15" alt="{{ gettext('{designation} is not starred').format(designation=source.journalist_designation) }}">
</button>
<input type="checkbox" name="cols_selected" value="{{ source.filesystem_id }}" aria-labelledby="un-starred-source-link-{{ loop_index }}">
<a href="/col/{{ source.filesystem_id }}" id="un-starred-source-link-{{ loop_index }}" class="code-name {% if source.num_unread != 0 %}unread{% else %}read{% endif %}">
{{ source.journalist_designation }}
</a>
<button class="button-star un-starred" type="submit"
formaction="{{ url_for('col.add_star', filesystem_id=source.filesystem_id) }}"
aria-label="{{ gettext('Star {designation}' ).format(designation=source.journalist_designation) }}">
<span>Star</span>
</button>
<input type="checkbox" name="cols_selected" value="{{ source.filesystem_id }}"
aria-labelledby="un-starred-source-link-{{ loop_index }}">
<a href="/col/{{ source.filesystem_id }}" id="un-starred-source-link-{{ loop_index }}"
class="code-name {% if source.num_unread != 0 %}unread{% else %}read{% endif %}">
{{ source.journalist_designation }}
</a>
{% endif %}
</div>
<div class="submission-count">
<input type="hidden" name="count-{{ source.journalist_designation|lower|replace(" ", "_") }}" class="submission-count-element" value="{{ docs + msgs }}">
<span>
<img src="{{ url_for('static', filename='icons/files.png') }}" class="icon-drop" width="14" height="16" alt="">
{{ ngettext('1 doc', '{num} docs', docs).format(num=docs) }}
</span>
<span>
<img src="{{ url_for('static', filename='icons/messages.png') }}" class="icon-drop" width="16" height="14" alt="">
{{ ngettext('1 message', '{num} messages', msgs).format(num=msgs) }}
</span>
<input type="hidden" name="count-{{ source.journalist_designation|lower|replace(" ", "_") }}"
class="submission-count-element" value="{{ docs + msgs }}">
</th>
<td class="submission-count files">
{{ ngettext('1 doc', '{num} docs', docs).format(num=docs) }}
</td>
<td class="submission-count messages">
{{ ngettext('1 message', '{num} messages', msgs).format(num=msgs) }}
</td>
<td class="unread">
{% if source.num_unread > 0 %}
<span class="unread">
<a class="btn small" href="/download_unread/{{ source.filesystem_id }}">
<img src="{{ url_for('static', filename='icons/download.png') }}" class="icon" width="11" height="13" alt="{{ gettext('Download') }}">
{{ ngettext('1 unread', '{num} unread', source.num_unread).format(num=source.num_unread) }}
</a>
</span>
<a class="btn small icon download" href="/download_unread/{{ source.filesystem_id }}">
{{ ngettext('1 unread', '{num} unread', source.num_unread).format(num=source.num_unread) }}
</a>
{% else %}
<span class="visually-hidden">
{{ ngettext('1 unread', '{num} unread', source.num_unread).format(num=source.num_unread) }}
</span>
{% endif %}
</div>
</li>
</td>
<td class="date" aria-label="{{ source.last_updated|rel_datetime_format(relative=True) }}"><time
title="{{ source.last_updated|rel_datetime_format }}"
datetime="{{ source.last_updated|html_datetime_format }}">{{ source.last_updated|rel_datetime_format(relative=True) }}</time>
</td>
</tr>
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
<div id="{{ modal_data.modal_id }}" class="menu-modal-dialog">
<a href="#close" class="external"></a>
<div id="delete-confirm-menu-dialog">
<p>
<a href="#close" class="external" aria-label="{{ gettext('Close') }}"></a>
<dialog open id="delete-confirm-menu-dialog" aria-labelledby="confirm-heading">
<h2 id="confirm-heading" class="paragraph-spacing">
{{ gettext('When the account for a source is deleted:') }}
<ul>
<li>{{ gettext('The source will no longer be able to log in with their codename.') }}</li>
<li>{{ gettext('You will not be able to send them replies.') }}</li>
<li>{{ gettext('All files and messages from that source will also be destroyed.') }}</li>
</ul>
</p>
<p>
<span class="modal-danger-text">{{ gettext('Are you sure this is what you want?') }}</span</p>
</h2>
<ul>
<li>{{ gettext('The source will no longer be able to log in with their codename.') }}</li>
<li>{{ gettext('You will not be able to send them replies.') }}</li>
<li>{{ gettext('All files and messages from that source will also be destroyed.') }}</li>
</ul>
<p class="modal-danger-text">{{ gettext('Are you sure this is what you want?') }}</p>
<div class="btn-row">
<a href="#close" id="cancel-collections-deletions" title="{{ gettext('Cancel') }}" class="btn cancel small">{{ gettext('Cancel') }}</a>
<button type="submit" id="delete-collections-confirm" name="action" value="delete" class="btn small danger">{{ gettext('Yes, Delete Selected Source Accounts') }}</button>
<a href="#close" id="cancel-collections-deletions" title="{{ gettext('Cancel') }}"
class="btn cancel small">{{ gettext('Cancel') }}</a>
<button type="submit" id="delete-collections-confirm" name="action" value="delete"
class="btn small danger">{{ gettext('Yes, Delete Selected Source Accounts') }}</button>
</div>
</div>
</dialog>
</div>
48 changes: 20 additions & 28 deletions securedrop/journalist_templates/_sources_confirmation_modal.html
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
<div id="{{ modal_data.modal_id }}" class="menu-modal-dialog">
<a href="#close" class="external"></a>
<div id="delete-menu-dialog">
<div id="delete-menu-no-select">
<p class="modal-danger-text">
<span class="modal-danger-text">{{ gettext("Nothing Selected") }}</span>
</p>
<p>
{{ gettext("You must select one or more items for deletion.") }}
</p>
<a href="#close" class="external" aria-label="{{ gettext('Close') }}"></a>
<dialog open id="delete-menu-dialog">
<div id="delete-menu-no-select" aria-labelledby="nothing-selected-heading">
<h2 id="nothing-selected-heading" class="modal-danger-text paragraph-spacing">{{ gettext("Nothing Selected") }}
</h2>
<p>{{ gettext("You must select one or more items for deletion.") }}</p>
</div>
<div id="delete-menu-cta">
<p>
<span id="delete-menu-summary"></span>
</p>
<p>
{{ gettext("What would you like to delete?") }}
</p>
<p>
<button id="delete-files-and-messages" type="submit" name="action" value="delete-data" class="small btn danger modal-stacked">{{ gettext('Files and Messages') }}</button>
</p>
<p>
<a href="#delete-sources-confirm-modal" id="delete-collections">
<button type="button" class="small danger btn modal-stacked">{{ gettext('Source Accounts') }}</button>
</a>
</p>
<div id="delete-menu-cta" aria-labelledby="delete-heading">
<p id="delete-menu-summary"></p>
<h2 id="delete-heading" class="paragraph-spacing">{{ gettext("What would you like to delete?") }}</h2>
<ul class="modal-stacked">
<li><button id="delete-files-and-messages" type="submit" name="action" value="delete-data"
class="small btn danger">{{ gettext('Files and Messages') }}</button></li>
<li>
<a href="#delete-sources-confirm-modal" id="delete-collections" class="small danger btn">
{{ gettext('Source Accounts') }}
</a>
</li>
</ul>
</div>
<p>
<a href="#close" id="delete-menu-dialog-cancel">{{ gettext('Cancel') }}</a>
</p>
</div>
<a href="#close" id="delete-menu-dialog-cancel" class="paragraph-spacing">{{ gettext('Cancel') }}</a>
</dialog>
</div>
11 changes: 7 additions & 4 deletions securedrop/journalist_templates/account_edit_hotp_secret.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
{% extends "base.html" %}
{% block body %}
<h1>{{ gettext('Change Secret') }}</h1>
<form method="post">
<input name="csrf_token" type="hidden" value="{{ csrf_token() }}">
<p>
<div>
<label for="otp_secret">{{ gettext('Change Secret') }}</label>
<input name="otp_secret" type="text" placeholder="{{ gettext('HOTP Secret') }}"><br>
</p>
<button type="submit">{{ gettext('CONTINUE') }}</button>
<input id="otp_secret" name="otp_secret" type="text" placeholder="{{ gettext('HOTP Secret') }}">
</div>
<div class="section-spacing">
<button type="submit" aria-label="{{ gettext('Continue to change secret') }}">{{ gettext('CONTINUE') }}</button>
</div>
</form>
{% endblock %}
15 changes: 10 additions & 5 deletions securedrop/journalist_templates/account_new_two_factor.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@
{% block body %}
{% if user.is_totp %}
<h1>{{ gettext('Enable FreeOTP') }}</h1>
<p>{{ gettext("You're almost done! To finish resetting your two-factor authentication, follow the instructions below to set up FreeOTP. Once you've added the entry for your account in the app, enter one of the 6-digit codes from the app to confirm that two-factor authentication is set up correctly.") }}</p>
<p>
{{ gettext("You're almost done! To finish resetting your two-factor authentication, follow the instructions below to set up FreeOTP. Once you've added the entry for your account in the app, enter one of the 6-digit codes from the app to confirm that two-factor authentication is set up correctly.") }}
</p>

<ol>
<li>{{ gettext('Install FreeOTP on your phone') }}</li>
<li>{{ gettext('Open the FreeOTP app') }}</li>
<li>{{ gettext('Tap the QR code symbol at the top') }}</li>
<li>{{ gettext('Your phone will now be in "scanning" mode. When you are in this mode, scan the barcode below:') }}</li>
<li>{{ gettext('Your phone will now be in "scanning" mode. When you are in this mode, scan the barcode below:') }}
</li>
</ol>
<div id="qrcode-container">{{ user.shared_secret_qrcode }}</div>
<p>{{ gettext("Can't scan the barcode? You can manually pair FreeOTP with your SecureDrop account by entering the following two-factor secret into the app:") }}
<p class="center"><span id="shared-secret">{{ user.formatted_otp_secret }}</span></p>
<p>
{{ gettext("Can't scan the barcode? You can manually pair FreeOTP with your SecureDrop account by entering the following two-factor secret into the app:") }}
</p>
<mark id="shared-secret">{{ user.formatted_otp_secret }}</mark>
<p>{{ gettext("Once you have paired FreeOTP with this account, enter the 6-digit verification code below:") }}</p>
{% else %}
<h1>{{ gettext('Enable YubiKey (OATH-HOTP)') }}</h1>
Expand All @@ -22,6 +27,6 @@ <h1>{{ gettext('Enable YubiKey (OATH-HOTP)') }}</h1>
<input name="csrf_token" type="hidden" value="{{ csrf_token() }}">
<label for="token">{{ gettext('Verification code') }}</label>
<input name="token" id="token" type="text" placeholder="123456">
<button type="submit">{{ gettext('SUBMIT') }}</button>
<button type="submit" aria-label="{{ gettext('Submit verification code') }}">{{ gettext('SUBMIT') }}</button>
</form>
{% endblock %}
Loading