templates: Mark all void tags as self-closing.

This reverses the policy that was set, but incompletely enforced, by
commit 951514dd7d.  The self-closing tag
syntax is clearer, more consistent, simpler to parse, compatible with
XML, preferred by Prettier, and (most importantly now) required by
FormatJS.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2021-04-20 15:46:14 -07:00 committed by Tim Abbott
parent 7177529107
commit dd3fa4ac52
84 changed files with 345 additions and 346 deletions

View File

@ -584,8 +584,8 @@ class TestSupportEndpoint(ZulipTestCase):
'<span class="label">user</span>\n',
f"<h3>{full_name}</h3>",
f"<b>Email</b>: {email}",
"<b>Is active</b>: True<br>",
f"<b>Role</b>: {role}<br>",
"<b>Is active</b>: True<br />",
f"<b>Role</b>: {role}<br />",
],
html_response,
)

View File

@ -1,11 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta charset="UTF-8" />
<title>Zulip - 500 internal server error</title>
<base href="/static/webpack-bundles/">
<base href="/static/webpack-bundles/" />
<meta http-equiv="refresh" content="60;URL='/'">
<meta http-equiv="refresh" content="60;URL='/'" />
</head>

View File

@ -39,7 +39,7 @@
<div class="input-group">
<label for="profile_field_choices_edit">{{t "Field choices" }}</label>
<div class="profile-field-choices" name="profile_field_choices_edit">
<hr>
<hr />
<div class="edit_profile_field_choices_container">
{{#each choices}}
{{> settings/profile_field_choice }}

View File

@ -5,9 +5,9 @@
{{/if}}
</span>
{{#if second_line}}
<br><span class="tooltip_inner_content">{{second_line}}</span>
<br /><span class="tooltip_inner_content">{{second_line}}</span>
{{/if}}
{{#if third_line}}
<br><span class="tooltip_inner_content">{{third_line}}</span>
<br /><span class="tooltip_inner_content">{{third_line}}</span>
{{/if}}
</div>

View File

@ -8,7 +8,7 @@
<a role="button" class="compose_control_button fa fa-smile-o" aria-label="{{t 'Add emoji' }}" id="emoji_map" tabindex=0 title="{{t 'Add emoji' }}"></a>
{{#if giphy_api_available }}
<a role="button" class="compose_control_button" aria-label="{{t 'Add GIF' }}" id="compose_box_giphy_grid" title="{{t 'Add GIF' }}">
<img class="compose_giphy_logo" tabindex=0 src="/static/images/GIPHY_logo.png">
<img class="compose_giphy_logo" tabindex=0 src="/static/images/GIPHY_logo.png" />
</a>
{{/if}}
<a class="message-control-link drafts-link" href="#drafts" title="{{t 'Drafts' }} (d)">{{t 'Drafts' }}</a>

View File

@ -2,7 +2,7 @@
<div class="arrow"></div>
<div class="popover-inner">
<div class="search-box">
<input type="text" tabindex=0 id="giphy-search-query" class="search-query" placeholder="{{t 'Search GIFs' }}">
<input type="text" tabindex=0 id="giphy-search-query" class="search-query" placeholder="{{t 'Search GIFs' }}" />
<button type="button" class="btn clear_search_button" id="giphy_search_clear">
<i class="fa fa-remove" aria-hidden="true"></i>
</button>
@ -15,7 +15,7 @@
<div class="giphy-content"></div>
</div>
<div class="popover-footer">
<img src="/static/images/GIPHY_attribution.png" alt="{{t 'GIPHY attribution' }}">
<img src="/static/images/GIPHY_attribution.png" alt="{{t 'GIPHY attribution' }}" />
</div>
</div>
</div>

View File

@ -7,7 +7,7 @@
<p class="hotspot-description">{{description}}</p>
</div>
<div class="hotspot-popover-bottom">
<img class="hotspot-img" alt=_("hotspot illustration") src="{{img}}">
<img class="hotspot-img" alt=_("hotspot illustration") src="{{img}}" />
<button class="hotspot-confirm">{{t 'Got it!' }}</button>
</div>
</div>

View File

@ -1,7 +1,7 @@
<div class="hotspot-inline trailing_bookend bookend hotspot-message" id="hotspot_intro_reply_icon">
<div class="hotspot-inline-top"><h1 class="hotspot-title">Send a reply</h1></div>
<div class="hotspot-inline-left">
<img class="hotspot-img" alt="" src="/static/images/hotspots/whale.svg">
<img class="hotspot-img" alt="" src="/static/images/hotspots/whale.svg" />
</div>
<div class="hotspot-inline-right">
<p>{{t 'Click anywhere on a message to reply.' }}</p>

View File

@ -24,7 +24,7 @@
</tbody>
</table>
</div>
<hr>
<hr />
<a href="/help/format-your-message-using-markdown" target="_blank" rel="noopener noreferrer">{{t "Detailed message formatting documentation" }}</a>
</div>
</div>

View File

@ -20,13 +20,13 @@
<input type="text" placeholder="{{topic}}" value="{{topic}}" class="message_edit_topic" id="message_edit_topic" />
<div class="message_edit_breadcrumb_messages" style='display:none;'>
<label class="checkbox">
<input class="send_notification_to_new_thread" name="send_notification_to_new_thread" type="checkbox" {{#if notify_new_thread}}checked="checked"{{/if}}>
<input class="send_notification_to_new_thread" name="send_notification_to_new_thread" type="checkbox" {{#if notify_new_thread}}checked="checked"{{/if}} />
<span></span>
</label>
<label for="send_notification_to_new_thread">{{t "Send notification to new topic" }}</label>
<div class="break-row"></div> <!-- break -->
<label class="checkbox">
<input class="send_notification_to_old_thread" name="send_notification_to_old_thread" type="checkbox" {{#if notify_old_thread}}checked="checked"{{/if}}>
<input class="send_notification_to_old_thread" name="send_notification_to_old_thread" type="checkbox" {{#if notify_old_thread}}checked="checked"{{/if}} />
<span></span>
</label>
<label for="send_notification_to_old_thread">{{t "Send notification to old topic" }}</label>
@ -70,7 +70,7 @@
<a role="button" tabindex=0 class="compose_control_button fa fa-smile-o" aria-label="{{t 'Add emoji' }}" id="emoji_map" data-message-id="{{message_id}}" title="{{t 'Add emoji' }}"></a>
{{#if giphy_api_available }}
<a role="button" class="compose_control_button" aria-label="{{t 'Add GIF' }}" id="compose_box_giphy_grid" title="{{t 'Add GIF' }}">
<img class="compose_giphy_logo" tabindex=0 src="/static/images/GIPHY_logo.png" data-message-id="{{message_id}}">
<img class="compose_giphy_logo" tabindex=0 src="/static/images/GIPHY_logo.png" data-message-id="{{message_id}}" />
</a>
{{/if}}
<a role="button" tabindex=0 class="message-control-link" data-overlay-trigger="message-formatting" >{{t 'Help' }}</a>

View File

@ -14,5 +14,5 @@
{{/if}}
<div class="message_author"><div class="author_details">{{ posted_or_edited }} {{ edited_by }}</div></div>
</div>
<hr>
<hr />
{{/each}}

View File

@ -20,17 +20,17 @@
{{/each}}
</select>
<i class="fa fa-angle-right" aria-hidden="true"></i>
<input name="new_topic_name" type="text" class="inline_topic_edit" value="{{topic_name}}">
<input name="old_topic_name" type="hidden" class="inline_topic_edit" value="{{topic_name}}">
<input name="current_stream_id" type="hidden" value="{{current_stream_id}}">
<input name="new_topic_name" type="text" class="inline_topic_edit" value="{{topic_name}}" />
<input name="old_topic_name" type="hidden" class="inline_topic_edit" value="{{topic_name}}" />
<input name="current_stream_id" type="hidden" value="{{current_stream_id}}" />
<div class="topic_move_breadcrumb_messages new-style">
<label class="checkbox">
<input class="send_notification_to_new_thread" name="send_notification_to_new_thread" type="checkbox" {{#if notify_new_thread}}checked="checked"{{/if}}>
<input class="send_notification_to_new_thread" name="send_notification_to_new_thread" type="checkbox" {{#if notify_new_thread}}checked="checked"{{/if}} />
<span></span>
</label>
<label for="send_notification_to_new_thread">{{t "Send notification to new topic" }}</label>
<label class="checkbox">
<input class="send_notification_to_old_thread" name="send_notification_to_old_thread" type="checkbox" {{#if notify_old_thread}}checked="checked"{{/if}}>
<input class="send_notification_to_old_thread" name="send_notification_to_old_thread" type="checkbox" {{#if notify_old_thread}}checked="checked"{{/if}} />
<span></span>
</label>
<label for="send_notification_to_old_thread">{{t "Send notification to old topic" }}</label>

View File

@ -17,7 +17,7 @@
{{/each}}
</div>
<br>
<br />
<input class="add-user-list-filter" name="user_list_filter" type="text"
autocomplete="off" placeholder="{{t "Filter" }}" />

View File

@ -3,7 +3,7 @@
{{> recent_topics_filters}}
</div>
<div class="search_group" role="group">
<input type="text" id="recent_topics_search" value="{{ search_val }}" placeholder="{{t 'Filter topics (t)' }}">
<input type="text" id="recent_topics_search" value="{{ search_val }}" placeholder="{{t 'Filter topics (t)' }}" />
<button type="button" class="btn clear_search_button" id="recent_topics_search_clear">
<i class="fa fa-remove" aria-hidden="true"></i>
</button>

View File

@ -184,7 +184,7 @@
</div>
</div>
<hr class="settings_separator">
<hr class="settings_separator" />
<div class="form-horizontal" id="api_key_button_box">
<h3>{{t "API key" }}</h3>

View File

@ -11,7 +11,7 @@
<div class="settings-section-title">{{t "Add new default stream" }}</div>
<div class="inline-block" id="default_stream_inputs">
<label for="default_stream_name">{{t "Stream name" }}</label>
<input class="create_default_stream" type="text" placeholder="{{t "Stream name" }}" name="stream_name" autocomplete="off" aria-label="{{t "Stream name" }}">
<input class="create_default_stream" type="text" placeholder="{{t "Stream name" }}" name="stream_name" autocomplete="off" aria-label="{{t "Stream name" }}" />
</div>
<div class="inline-block">
<button type="submit" id="do_submit_stream" class="button rounded sea-green">{{t "Add stream" }}</button>

View File

@ -11,7 +11,7 @@
</p>
</li>
<hr>
<hr />
{{! tabindex="0" Makes anchor tag focusable. Needed for keyboard support. }}
<li>

View File

@ -4,7 +4,7 @@
{{/if}}
<a data-user-id="{{user_id}}" class="view_user_profile">{{full_name}}</a>{{#unless @last}},{{else}}.{{/unless}}
{{/each}}
<br>
<br />
{{#each already_subscribed_users}}
{{#if @first}}
{{t "Already subscribed users:" }}

View File

@ -6,7 +6,7 @@
</p>
</li>
<hr>
<hr />
{{#if can_mute_topic}}
<li>

View File

@ -5,7 +5,7 @@
{{group_description}}
</div>
</div>
<hr>
<hr />
<ul class="nav nav-list member-list" data-simplebar data-simplebar-auto-hide="false">
{{#each members}}
<li>
@ -20,7 +20,7 @@
</li>
{{/each}}
</ul>
<hr>
<hr />
<ul class="nav nav-list manage-group">
<li>
<a href="#organization/user-groups-admin">

View File

@ -52,7 +52,7 @@
</div>
{{#if is_me}}
<hr>
<hr />
{{#if can_set_away}}
<li>
<a tabindex="0" class="set_away_status">
@ -80,7 +80,7 @@
{{/if}}
{{#if status_text}}
{{#unless is_me}}<hr>{{/unless}}
{{#unless is_me}}<hr />{{/unless}}
<li class="user_info_status_text">
<span id="status_message">
{{status_text}}
@ -89,7 +89,7 @@
</li>
{{/if}}
<hr>
<hr />
{{#if show_user_profile}}
<li>
<a tabindex="0" class="view_full_user_profile">
@ -134,7 +134,7 @@
</a>
</li>
{{/if}}
<hr>
<hr />
<li>
<a href="{{ pm_with_uri }}" class="narrow_to_private_messages">
<i class="fa fa-lock" aria-hidden="true"></i>

View File

@ -14,7 +14,7 @@
</a>
{{/if}}
</div>
<br>
<br />
{{#if show_email}}
<div id="email" class="default-field">
<span class="name">{{#tr}}Email{{/tr}}</span>
@ -40,7 +40,7 @@
</div>
{{/if}}
</div>
<hr>
<hr />
<div id="content">
{{#each profile_data}}
<div data-type="{{this.type}}" class="field-section custom_user_field" data-field-id="{{this.id}}">

View File

@ -18,5 +18,5 @@
<input type="text" class="poll-option" placeholder="{{t 'New choice'}}" />
<button class="poll-option">{{t "Add choice" }}</button>
</div>
<br>
<br />
</div>

View File

@ -1,4 +1,4 @@
<br>
<br />
{{#each pending_tasks}}
<li>
<button class="task" data-key="{{ key }}">

View File

@ -2,7 +2,7 @@
{% block customhead %}
{{ super() }}
<meta http-equiv="refresh" content="60;URL='/'">
<meta http-equiv="refresh" content="60;URL='/'" />
{% endblock %}
{% block portico_class_name %}error{% endblock %}

View File

@ -12,13 +12,13 @@
{% if not is_home %}
<a class="show-all" href="/activity">Home</a>
<br>
<br />
{% endif %}
<h4>{{ title }}</h4>
{% if realm_link %}
<a href="{{ realm_link }}">Graph</a><br>
<a href="{{ realm_link }}">Graph</a><br />
{% endif %}
<ul class="nav nav-tabs">

View File

@ -1,9 +1,9 @@
<span class="label">realm</span>
<h3><img src="{{ realm_icon_url(realm) }}" class="support-realm-icon"> {{ realm.name }}</h3>
<h3><img src="{{ realm_icon_url(realm) }}" class="support-realm-icon" /> {{ realm.name }}</h3>
<b>URL</b>: <a target="_blank" rel="noopener noreferrer" href="{{ realm.uri }}">{{ realm.uri }}</a> |
<a target="_blank" rel="noopener noreferrer" href="/stats/realm/{{ realm.string_id }}/">stats</a> |
<a target="_blank" rel="noopener noreferrer" href="/realm_activity/{{ realm.string_id }}/">activity</a><br>
<b>Date created</b>: {{ realm.date_created|timesince }} ago<br>
<a target="_blank" rel="noopener noreferrer" href="/realm_activity/{{ realm.string_id }}/">activity</a><br />
<b>Date created</b>: {{ realm.date_created|timesince }} ago<br />
{% set owner_emails_string = get_realm_owner_emails_as_string(realm) %}
<b>Owners</b>: {{ owner_emails_string }}
{% if owner_emails_string %}
@ -11,7 +11,7 @@
<i class="fa fa-copy"></i>
</a>
{% endif %}
<br>
<br />
{% set admin_emails_string = get_realm_admin_emails_as_string(realm) %}
<b>Admins</b>: {{ admin_emails_string }}
{% if admin_emails_string %}
@ -19,7 +19,7 @@
<i class="fa fa-copy"></i>
</a>
{% endif %}
<br>
<br />
{% set first_human_user = realm.get_first_human_user() %}
{% if first_human_user %}
<b>First human user</b>: {{ first_human_user.delivery_email }}
@ -31,7 +31,7 @@
{% endif %}
<form method="POST" class="support-realm-status-form">
<b>Status</b>:<br>
<b>Status</b>:<br />
{{ csrf_input }}
<input type="hidden" name="realm_id" value="{{ realm.id }}" />
<select name="status">
@ -41,14 +41,14 @@
<button type="submit" class="button rounded small support-submit-button">Update</button>
</form>
<form method="POST">
<b>New subdomain</b>:<br>
<b>New subdomain</b>:<br />
{{ csrf_input }}
<input type="hidden" name="realm_id" value="{{ realm.id }}" />
<input type="text" name="new_subdomain" required>
<input type="text" name="new_subdomain" required />
<button type="submit" class="button rounded small support-submit-button">Update</button>
</form>
<form method="POST" class="support-plan-type-form">
<b>Plan type</b>:<br>
<b>Plan type</b>:<br />
{{ csrf_input }}
<input type="hidden" name="realm_id" value="{{ realm.id }}" />
<select name="plan_type">
@ -60,7 +60,7 @@
<button type="submit" class="button rounded small support-submit-button">Update</button>
</form>
<form method="POST" class="sponsorship-pending-form">
<b>Sponsorship pending</b>:<br>
<b>Sponsorship pending</b>:<br />
{{ csrf_input }}
<input type="hidden" name="realm_id" value="{{ realm.id }}" />
<select name="sponsorship_pending">
@ -83,35 +83,35 @@
{% endif %}
<form method="POST" class="support-discount-form">
<b>Discount (use 85 for nonprofits)</b>:<br>
<b>Discount (use 85 for nonprofits)</b>:<br />
{{ csrf_input }}
<input type="hidden" name="realm_id" value="{{ realm.id }}" />
{% if realm.current_plan and realm.current_plan.fixed_price %}
<input type="number" name="discount" value="{{ get_discount_for_realm(realm) }}" disabled>
<input type="number" name="discount" value="{{ get_discount_for_realm(realm) }}" disabled />
<button type="submit" class="button rounded small support-submit-button" disabled>Update</button>
{% else %}
<input type="number" name="discount" value="{{ get_discount_for_realm(realm) }}" required>
<input type="number" name="discount" value="{{ get_discount_for_realm(realm) }}" required />
<button type="submit" class="button rounded small support-submit-button">Update</button>
{% endif %}
</form>
{% if realm.current_plan %}
<div class="current-plan-details">
<h3>📅 Current plan</h3>
<b>Name</b>: {{ realm.current_plan.name }}<br>
<b>Status</b>: {{realm.current_plan.get_plan_status_as_text()}}<br>
<b>Billing schedule</b>: {% if realm.current_plan.billing_schedule == realm.current_plan.ANNUAL %}Annual{% else %}Monthly{% endif %}<br>
<b>Licenses</b>: {{ realm.current_plan.licenses_used }}/{{ realm.current_plan.licenses }} ({% if realm.current_plan.automanage_licenses %}Automatic{% else %}Manual{% endif %})<br>
<b>Name</b>: {{ realm.current_plan.name }}<br />
<b>Status</b>: {{realm.current_plan.get_plan_status_as_text()}}<br />
<b>Billing schedule</b>: {% if realm.current_plan.billing_schedule == realm.current_plan.ANNUAL %}Annual{% else %}Monthly{% endif %}<br />
<b>Licenses</b>: {{ realm.current_plan.licenses_used }}/{{ realm.current_plan.licenses }} ({% if realm.current_plan.automanage_licenses %}Automatic{% else %}Manual{% endif %})<br />
{% if realm.current_plan.price_per_license %}
<b>Price per license</b>: ${{ realm.current_plan.price_per_license/100 }}<br>
<b>Price per license</b>: ${{ realm.current_plan.price_per_license/100 }}<br />
{% else %}
<b>Fixed price</b>: ${{ realm.current_plan.fixed_price/100 }}<br>
<b>Fixed price</b>: ${{ realm.current_plan.fixed_price/100 }}<br />
{% endif %}
<b>Next invoice date</b>: {{ realm.current_plan.next_invoice_date.strftime('%d %B %Y') }}<br>
<b>Next invoice date</b>: {{ realm.current_plan.next_invoice_date.strftime('%d %B %Y') }}<br />
</div>
<form method="POST" class="billing-method-form">
<br>
<b>Billing method</b><br>
<br />
<b>Billing method</b><br />
{{ csrf_input }}
<input type="hidden" name="realm_id" value="{{ realm.id }}" />
<select name="billing_method" class="billing-method-select" required>
@ -122,8 +122,8 @@
</form>
<form method="POST" class="downgrade-plan-form">
<br>
<b>Downgrade plan</b><br>
<br />
<b>Downgrade plan</b><br />
{{ csrf_input }}
<input type="hidden" name="realm_id" value="{{ realm.id }}" />
<select name="downgrade_method" class="downgrade-plan-method-select" required>

View File

@ -125,7 +125,7 @@
<span class="last_update_tooltip" data-tippy-content="{% trans %}A full update of all the graphs happens once a day. The “messages sent over time” graph is updated once an hour.{% endtrans %}">
<span class="fa fa-info-circle" id="id_last_update_question_sign"></span>
</span>
<br>
<br />
<span class="docs_link"><a href="/help/analytics">{{ _("Analytics documentation") }}</a></span>
</div>
</div>

View File

@ -9,10 +9,10 @@
{% block content %}
<div class="container">
<br>
<br />
<form class="new-style">
<center>
<input type="text" name="q" class="input-xxlarge search-query" placeholder="full names, emails, string_ids, organization URLs separated by commas" value="{{ request.GET.get('q', '') }}" autofocus>
<input type="text" name="q" class="input-xxlarge search-query" placeholder="full names, emails, string_ids, organization URLs separated by commas" value="{{ request.GET.get('q', '') }}" autofocus />
<button type="submit" class="button small support-search-button">Search</button>
</center>
</form>
@ -37,15 +37,15 @@
<div class="support-query-result new-style">
<span class="label">user</span>
<h3>{{ user.full_name }}</h3>
<b>Email</b>: {{ user.delivery_email }}<br>
<b>Date joined</b>: {{ user.date_joined|timesince }} ago<br>
<b>Is active</b>: {{ user.is_active }}<br>
<b>Role</b>: {{ user.get_role_name() }}<br>
<hr>
<b>Email</b>: {{ user.delivery_email }}<br />
<b>Date joined</b>: {{ user.date_joined|timesince }} ago<br />
<b>Is active</b>: {{ user.is_active }}<br />
<b>Role</b>: {{ user.get_role_name() }}<br />
<hr />
<div>
{% include "analytics/realm_details.html" %}
</div>
<hr>
<hr />
</div>
{% endfor %}
@ -83,32 +83,32 @@
{% set realm = object %}
{% set show_realm_details = False %}
{% endif %}
<br>
<br>
<br />
<br />
{% if email %}
<b>Email</b>: {{ email }}<br>
<b>Email</b>: {{ email }}<br />
{% endif %}
<b>Link</b>: {{ confirmation.url }}
<a title="Copy link" class="copy-button" data-copytext="{{ confirmation.url }}">
<i class="fa fa-copy"></i>
</a><br>
<b>Expires in</b>: {{ confirmation.expires_in }}<br>
</a><br />
<b>Expires in</b>: {{ confirmation.expires_in }}<br />
{% if confirmation.link_status %}
<b>Status</b>: {{ confirmation.link_status }}
{% endif %}
<br>
<br />
{% if show_realm_details %}
<hr>
<hr />
<div>
{% include "analytics/realm_details.html" %}
</div>
{% elif realm %}
<b>Realm</b>: {{ realm.string_id }}
<br>
<br />
{% endif %}
<br>
<br />
</div>
<br>
<br />
{% endfor %}
</div>
</div>

View File

@ -2,7 +2,7 @@
{% set entrypoint = "billing" %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://checkout.stripe.com/checkout.js"></script>
{% endblock %}
@ -26,7 +26,7 @@
<li><a data-toggle="tab" href="#settings">Settings</a></li>
</ul>
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}" />
<div class="tab-content">
<div class="tab-pane active" id="overview">
{% if free_trial %}
@ -133,13 +133,13 @@
</div>
<div id="goto-zulip-organization-link">
{% if onboarding %}
<br>
<br />
<h3>
<b><a href="/">Go to your Zulip organization</a></b>
</h3>
{% endif %}
</div>
<hr>
<hr />
<div class="support-link">
<p>
Contact <a href="mailto:support@zulip.com">support@zulip.com</a>

View File

@ -6,7 +6,7 @@
{% endblock %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}
@ -176,8 +176,8 @@
</li>
</ul>
<br>
<hr>
<br />
<hr />
<h2 id="fullstack">Senior Full Stack Engineer</h2>
<p>
@ -245,8 +245,8 @@
</li>
</ul>
<br>
<hr>
<br />
<hr />
<h2>How to apply for a job</h2>
<p>

View File

@ -2,7 +2,7 @@
{% set entrypoint = "upgrade" %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://checkout.stripe.com/checkout.js"></script>
{% endblock %}
@ -37,18 +37,18 @@
<li><a data-toggle="tab" href="#sponsorship">💚 Request sponsorship</a></li>
</ul>
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}" />
<div class="tab-content">
<div class="tab-pane active" id="autopay">
<div id="autopay-input-section">
<form id="autopay-form" data-key="{{ publishable_key }}" data-email="{{ email }}" method="post">
<input type="hidden" name="seat_count" value="{{ seat_count }}">
<input type="hidden" name="signed_seat_count" value="{{ signed_seat_count }}">
<input type="hidden" name="salt" value="{{ salt }}">
<input type="hidden" name="billing_modality" value="charge_automatically">
<input type="hidden" name="seat_count" value="{{ seat_count }}" />
<input type="hidden" name="signed_seat_count" value="{{ signed_seat_count }}" />
<input type="hidden" name="salt" value="{{ salt }}" />
<input type="hidden" name="billing_modality" value="charge_automatically" />
{% if onboarding and free_trial_days %}
<p><b>Not ready to start your trial?</b> <a href="/">Continue with the Zulip Cloud Free plan</a>.</p>
<hr>
<hr />
<h2>Zulip Standard free trial</h2>
{% endif %}
<div class="payment-schedule">
@ -104,7 +104,7 @@
{% if free_trial_days %}
After the Free Trial, you&rsquo;ll be charged
<b>$<span id="charged_amount"></span></b> for <b>{{ seat_count }}</b>
users (or more if you later add more users).<br>
users (or more if you later add more users).<br />
We'll automatically charge you for additional licenses as users
are added, and remove licenses not in use at the end of each billing
@ -112,31 +112,31 @@
{% else %}
You&rsquo;ll initially be charged
<b>$<span id="charged_amount"></span></b> for <b>{{ seat_count }}</b>
users.<br>
users.<br />
We'll automatically charge you for additional licenses as users
are added, and remove licenses not in use at the end of each billing
period.
{% endif %}
</p>
<input type="hidden" name="licenses" id="automatic_license_count" value="{{ seat_count }}">
<input type="hidden" name="licenses" id="automatic_license_count" value="{{ seat_count }}" />
</div>
<div id="license-manual-section">
<p>
{% if free_trial_days %}
Enter the number of users you would like to pay for after the Free Trial.<br>
Enter the number of users you would like to pay for after the Free Trial.<br />
You'll need to manually add licenses to add or invite
additional users.
{% else %}
Enter the number of users you would like to pay for.<br>
Enter the number of users you would like to pay for.<br />
You'll need to manually add licenses to add or invite
additional users.
{% endif %}
</p>
<h4>Number of licenses (minimum {{ seat_count }})</h4>
<input type="number" name="licenses" min="{{ seat_count }}" autocomplete="off" id="manual_license_count" required/><br>
<input type="number" name="licenses" min="{{ seat_count }}" autocomplete="off" id="manual_license_count" required/><br />
</div>
<button id="add-card-button" class="stripe-button-el">
@ -164,12 +164,12 @@
<form id="invoice-form" method="post">
{% if onboarding and free_trial_days %}
<p><b>Not ready to start your trial?</b> <a href="/">Continue with the Zulip Cloud Free plan</a>.</p>
<hr>
<hr />
<h2>Zulip Standard free trial</h2>
{% endif %}
<input type="hidden" name="signed_seat_count" value="{{ signed_seat_count }}">
<input type="hidden" name="salt" value="{{ salt }}">
<input type="hidden" name="billing_modality" value="send_invoice">
<input type="hidden" name="signed_seat_count" value="{{ signed_seat_count }}" />
<input type="hidden" name="salt" value="{{ salt }}" />
<input type="hidden" name="billing_modality" value="send_invoice" />
<div class="payment-schedule">
<h3>{{ _("Payment schedule") }}</h3>
<label>
@ -187,18 +187,18 @@
</div>
<p>
{% if free_trial_days %}
Enter the number of users you would like to pay for.<br>
Enter the number of users you would like to pay for.<br />
We'll email you an invoice after the free trial.
Invoices can be paid by ACH transfer or credit card.
{% else %}
Enter the number of users you would like to pay for.<br>
Enter the number of users you would like to pay for.<br />
We'll email you an invoice in 1-2 hours. Invoices can be paid by
ACH transfer or credit card.
{% endif %}
</p>
<h4>Number of licenses (minimum {{ min_invoiced_licenses }})</h4>
<input type="number" min="{{ min_invoiced_licenses }}" autocomplete="off"
id="invoiced_licenses" name="licenses" required/><br>
id="invoiced_licenses" name="licenses" required/><br />
<button type="submit" id="invoice-button" class="stripe-button-el invoice-button">Buy Standard</button>
</form>
</div>
@ -232,7 +232,7 @@
<option value="event">{{_('Event (hackathons, conferences, etc.)')}}</option>
<option value="other">{{_('Other')}}</option>
</select>
<br>
<br />
<label>
<h4>Organization website</h4>
</label>
@ -241,7 +241,7 @@
<h4>Describe your organization briefly</h4>
</label>
<textarea name="description" style="width: 100%;" cols="100" rows="5" required></textarea>
<br>
<br />
<p id="sponsorship-discount-details"></p>
<button type="submit" id="sponsorship-button" class="stripe-button-el invoice-button">Submit</button>
</form>

View File

@ -6,7 +6,7 @@
{% endblock %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}
@ -38,7 +38,7 @@
<li><p>Get your Zulip API key from the Zulip "Settings" panel and put it in a file in your
Athena home directory called <code>~/Private/.zulip-api-key</code>.</p></li>
<li><p>Run the following command to copy over all of your subscriptions:<br>
<li><p>Run the following command to copy over all of your subscriptions:<br />
<code>/mit/tabbott/zulip/zephyr_mirror.py --sync-subscriptions</code></p>
<p> <strong>NOTE</strong>: Zulip supports several ways to control what messages you want to read

View File

@ -48,10 +48,10 @@ the registration flow has its own (nearly identical) copy of the fields below in
{% endfor %}
{% endif %}
</div>
<br>
<br />
<div class="control-group">
<div class="controls">
<input type="submit" class="btn btn-primary" value="{{ _('Enter') }}" /><br>
<input type="submit" class="btn btn-primary" value="{{ _('Enter') }}" /><br />
<input type="hidden" name="next" value="{{ next }}" />
</div>
</div>

View File

@ -29,7 +29,7 @@ page can be easily identified in it's respective JavaScript file -->
</div>
<div class="invite-required">
<hr>
<hr />
<i class="fa fa-lock"></i>{{ _("You need an invitation to join this organization.") }}
</div>
</div>

View File

@ -159,6 +159,6 @@
<div id="compose" {% if embedded %}data-embedded{% endif %}>
</div>
<audio id="notification-sound-audio">
<source id="notification-sound-source-ogg" type="audio/ogg">
<source id="notification-sound-source-mp3" type="audio/mpeg">
<source id="notification-sound-source-ogg" type="audio/ogg" />
<source id="notification-sound-source-mp3" type="audio/mpeg" />
</audio>

View File

@ -4,12 +4,12 @@
{# Includes some other templates as tabs. #}
{% block meta_viewport %}
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
{% endblock %}
{% block customhead %}
<meta name="apple-mobile-web-app-capable" content="yes">
<link href="/static/images/logo/apple-touch-icon-precomposed.png" rel="apple-touch-icon-precomposed">
<meta name="apple-mobile-web-app-capable" content="yes" />
<link href="/static/images/logo/apple-touch-icon-precomposed.png" rel="apple-touch-icon-precomposed" />
<style>
#app-loading {
background-color: hsl(0, 0%, 100%);

View File

@ -328,7 +328,7 @@
</tr>
</table>
</div>
<hr>
<hr />
<a href="/help/keyboard-shortcuts" target="_blank" rel="noopener noreferrer">{% trans %}Detailed keyboard shortcuts documentation{% endtrans %}</a>
</div>
</div>

View File

@ -112,7 +112,7 @@
<span class="operator_value">{{ placeholder_keyword }}</span>.
{% endtrans %}
</p>
<hr>
<hr />
<a href="/help/search-for-messages#search-operators" target="_blank" rel="noopener noreferrer">{% trans %}Detailed search operators documentation{% endtrans %}</a>
</div>
</div>

View File

@ -2,7 +2,7 @@
{% set entrypoint = "landing-page" %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style media="screen">
.app.portico-page { padding-bottom: 0px; }
</style>

View File

@ -4,7 +4,7 @@
{# Base template for the whole site. #}
<head>
<meta charset="UTF-8">
<meta charset="UTF-8" />
{% block title %}
{% if user_profile and user_profile.realm.name %}
<title>{{user_profile.realm.name}} - Zulip</title>
@ -16,10 +16,10 @@
{% endif %}
{% endif %}
{% endblock %}
<link id="favicon" rel="icon" href="/static/images/favicon.svg?v=4">
<link rel="alternate icon" href="/static/images/favicon.png?v=4">
<link id="favicon" rel="icon" href="/static/images/favicon.svg?v=4" />
<link rel="alternate icon" href="/static/images/favicon.png?v=4" />
{% block meta_viewport %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% if not user_profile %}
{% include 'zerver/meta_tags.html' %}

View File

@ -9,7 +9,7 @@
<div class="errorbox config-error">
<div class="errorcontent">
<h1 class="lead">Configuration error</h1>
<br>
<br />
{% if error_name == "ldap_error_realm_is_none" %}
{% trans %}
You are trying to log in using LDAP without creating an

View File

@ -26,14 +26,14 @@
{{ _("Log in with another account") }}
</button>
</form>
<br>
<br />
<form class="form-inline" id="send_confirm" name="send_confirm"
action="{{ continue_link }}" method="get">
<button class="outline">
{{ _("Continue to registration") }}
</button>
{% if full_name %}
<input type="hidden" name="full_name" value="{{ full_name }}">
<input type="hidden" name="full_name" value="{{ full_name }}" />
{% endif %}
</form>
</div>

View File

@ -2,7 +2,7 @@
{% block customhead %}
{{ super() }}
<meta http-equiv="refresh" content="60;URL='/'">
<meta http-equiv="refresh" content="60;URL='/'" />
{% endblock %}
{% block portico_content %}

View File

@ -4,7 +4,7 @@
{% block content %}
<div class="flex new-style">
<div class="desktop-redirect-box white-box">
<img class="avatar desktop-redirect-image" src="{{ realm_icon_url }}" alt=""/><br>
<img class="avatar desktop-redirect-image" src="{{ realm_icon_url }}" alt=""/><br />
<p class="copy-token-info">{% trans %}Copy this login token and return to your Zulip app to finish logging in:{% endtrans %}</p>
<p>
<span class="input-box">

View File

@ -41,14 +41,14 @@
</tr>
<tr>
<td><a href="/emails">/emails</a></td>
<td><code>./scripts/setup/inline_email_css.py</code><br>
<td><code>./scripts/setup/inline_email_css.py</code><br />
Run the command if you made changes to source.html email templates.
</td>
<td>View outgoing and example emails.</td>
</tr>
<tr>
<td><a href="/stats/realm/analytics/">/stats/realm/analytics/</a></td>
<td><code>./manage.py populate_analytics_db</code><br>
<td><code>./manage.py populate_analytics_db</code><br />
Run the command after changing analytics data population logic.
</td>
<td>View the /stats page with some pre-populated data</td>

View File

@ -46,7 +46,7 @@
<label for="forward_address"><strong>Address to which emails should be forwarded</strong></label>
<input type="text" id="address" name="forward_address" placeholder="eg: your-email@example.com" value="{{forward_address}}"/>
</div>
<br>
<br />
<div class="alert alert-info">
You must set up SMTP as described
<a target="_blank" rel="noopener noreferrer" href="https://zulip.readthedocs.io/en/latest/subsystems/email.html#development-and-testing">

View File

@ -3,7 +3,7 @@
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
@ -67,21 +67,21 @@
</div>
</div>
<br>
<br />
<div class="row2">
<label for="URL"><b>URL</b> (Automatically Generated)</label>
<input id="URL" type="text" />
</div>
<br>
<br />
<div class="row3">
<label for="custom_http_headers"><b>Custom HTTP Headers</b></label>
<textarea id="custom_http_headers"></textarea>
</div>
<br>
<br />
<div class="row4">
<div class="col1">
@ -104,6 +104,6 @@
</div>
<div class="pad-small"></div>
<input id="csrftoken" type="hidden" value="{{ csrf_token }}">
<input id="csrftoken" type="hidden" value="{{ csrf_token }}" />
{% endblock %}

View File

@ -25,7 +25,7 @@
{{ render_markdown_path(article, api_uri_context) }}
<div id="footer" class="documentation-footer">
<hr>
<hr />
<p>We're here to help! Email us at <a href="mailto:{{ support_email }}">{{ support_email }}</a> with questions, feedback, or feature requests.</p>
</div>
</div>

View File

@ -23,4 +23,4 @@
<div class="email-text" style="display: none;">
<pre>{{ body }}</pre>
</div>
<hr>
<hr />

View File

@ -29,7 +29,7 @@
<p>{{ new_streams.html|display_list(1000)|safe }}.</p>
{% endif %}
<br>
<br />
<p><a href="{{ realm_uri }}">{% trans %}Click here to log in to Zulip and catch up.{% endtrans %}</a></p>
{% endblock %}

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta charset="UTF-8" />
<title>Zulip</title>
</head>
{% if has_preheader %}

View File

@ -17,15 +17,15 @@
<p>
{{ _('Your account details:') }}
<li>{% trans organization_url=macros.link_tag(realm_uri) %}Organization URL: {{ organization_url }}{% endtrans %}<br></li>
<li>{% trans organization_url=macros.link_tag(realm_uri) %}Organization URL: {{ organization_url }}{% endtrans %}<br /></li>
{% if ldap %}
{% if ldap_username %}
<li>{% trans %}Username: {{ ldap_username }}{% endtrans %}<br></li>
<li>{% trans %}Username: {{ ldap_username }}{% endtrans %}<br /></li>
{% else %}
<li>{{ _('Use your LDAP account to log in') }}<br></li>
<li>{{ _('Use your LDAP account to log in') }}<br /></li>
{% endif %}
{% else %}
<li>{% trans email=macros.email_tag(email) %}Email: {{ email }}{% endtrans %}<br></li>
<li>{% trans email=macros.email_tag(email) %}Email: {{ email }}{% endtrans %}<br /></li>
{% endif %}
{% trans apps_page_link="https://zulip.com/apps" %}(you'll need these to sign in to the <a href="{{ apps_page_link }}">mobile and desktop</a> apps){% endtrans %}
</p>
@ -39,7 +39,7 @@
</p>
<p>
{{ _("Cheers,") }}<br>
{{ _("Cheers,") }}<br />
{{ _("Team Zulip") }}
</p>

View File

@ -24,7 +24,7 @@
<p><a href="{{ realm_uri }}">{{ _("Take it for a spin now.") }}</a></p>
<p>{{ _("Thanks,") }}<br>{{ _("Zulip") }}</p>
<p>{{ _("Thanks,") }}<br />{{ _("Zulip") }}</p>
{% endblock %}
{% block manage_preferences %}

View File

@ -24,19 +24,19 @@
{% block manage_preferences %}
<div class="email-preferences">
&mdash;<br>
&mdash;<br />
{% if mention %}
{% trans %}You are receiving this because you were mentioned in {{ realm_name }}.{% endtrans %}<br>
{% trans %}You are receiving this because you were mentioned in {{ realm_name }}.{% endtrans %}<br />
{% elif stream_email_notify %}
{% trans %}You are receiving this because you have email notifications enabled for this stream.{% endtrans %}<br>
{% trans %}You are receiving this because you have email notifications enabled for this stream.{% endtrans %}<br />
{% endif %}
{% if reply_to_zulip %}
{% trans notif_url=realm_uri + "/#settings/notifications" %}Reply to this email directly, <a href="{{ narrow_url }}">view it in Zulip</a>, or <a href="{{ notif_url }}">manage email preferences</a>.{% endtrans %}
{% elif not show_message_content %}
{% trans notif_url=realm_uri + "/#settings/notifications" %}<a href="{{ narrow_url }}">View or reply in Zulip</a>, or <a href="{{ notif_url }}">manage email preferences</a>.{% endtrans %} <br>
{% trans notif_url=realm_uri + "/#settings/notifications" %}<a href="{{ narrow_url }}">View or reply in Zulip</a>, or <a href="{{ notif_url }}">manage email preferences</a>.{% endtrans %} <br />
{% else %}
{% trans notif_url=realm_uri + "/#settings/notifications" %}<a href="{{ narrow_url }}">Reply in Zulip</a>, or <a href="{{ notif_url }}">manage email preferences</a>.{% endtrans %} <br>
<br>
{% trans notif_url=realm_uri + "/#settings/notifications" %}<a href="{{ narrow_url }}">Reply in Zulip</a>, or <a href="{{ notif_url }}">manage email preferences</a>.{% endtrans %} <br />
<br />
{% trans url="https://zulip.readthedocs.io/en/latest/production/email-gateway.html" %}
Do not reply to this email. This Zulip server is not configured to accept incoming emails (<a href="{{ url }}">help</a>).
{% endtrans %}

View File

@ -10,7 +10,7 @@
{% trans new_email=macros.email_tag(new_email), support_email=macros.email_tag(support_email) %}The email associated with your Zulip account was recently changed to {{ new_email }}. If you did not request this change, please contact us immediately at {{ support_email }}.{% endtrans %}
</p>
<p>
{{ _("Best,") }}<br>
{{ _("Best,") }}<br />
{{ _("Team Zulip") }}
</p>
{% endblock %}

View File

@ -37,7 +37,7 @@
</p>
<p>
{{ _("Thanks,") }}<br>
{{ _("Thanks,") }}<br />
{{ _("Zulip Security") }}
</p>
{% endblock %}

View File

@ -3,21 +3,21 @@
{% block content %}
<b>Support URL</b>: <a href="{{ support_url }}">{{ support_url }}</a>
<br><br>
<br /><br />
<b>Website</b>: <a href="{{ website }}">{{ website }}</a>
<br><br>
<br /><br />
<b>Organization type</b>: {{ organization_type }}
<br><br>
<br /><br />
<b>Description</b>:
<br>
<br />
{{ description }}
<br><br>
<br /><br />
<b>Requested by</b>: {{ requested_by }} ({{ user_role }})

View File

@ -6,7 +6,7 @@
{% endblock %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}

View File

@ -9,7 +9,7 @@
{% endblock %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}
@ -104,7 +104,7 @@
</div>
</div>
<hr>
<hr />
<div class="company-container">
<header>

View File

@ -11,7 +11,7 @@ conversations and focus work.' %}
{% endblock %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}

View File

@ -6,7 +6,7 @@
{% endblock %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}

View File

@ -2,7 +2,7 @@
{% set entrypoint = "landing-page" %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
.portico-page {
@ -30,7 +30,7 @@
<h1>Chat for distributed teams.</h1>
<p>
Zulip combines the immediacy of real-time chat with an email
threading model. <br class="line-break-desktop">With Zulip, you can catch
threading model. <br class="line-break-desktop" />With Zulip, you can catch
up on important conversations while ignoring
irrelevant ones.
</p>
@ -44,7 +44,7 @@
<div class="item-inner">
<button data-target="#tour-carousel" data-slide="next" type="button" name="button" class="start-button">Take the tour</button>
<img src="/static/images/story-tutorial/zulip-topic-blurred.png" alt="" class="start-image">
<img src="/static/images/story-tutorial/zulip-topic-blurred.png" alt="" class="start-image" />
</div>
</div>
<div class="item">
@ -53,11 +53,11 @@
<div class="zulip-slack-comparison">
<div class="comparison-zulip">
<div class="caption">Zulip</div>
<img src="/static/images/story-tutorial/zulip-streams.png" class="zulip-streams" alt="{{ _('Streams in Zulip') }}">
<img src="/static/images/story-tutorial/zulip-streams.png" class="zulip-streams" alt="{{ _('Streams in Zulip') }}" />
</div>
<div class="comparison-slack">
<div class="caption">Other team chat</div>
<img src="/static/images/story-tutorial/slack-streams.png" class="slack-streams" alt="{{ _('Streams in Slack') }}">
<img src="/static/images/story-tutorial/slack-streams.png" class="slack-streams" alt="{{ _('Streams in Slack') }}" />
</div>
</div>
</div>
@ -68,11 +68,11 @@
<div class="zulip-slack-comparison">
<div class="comparison-zulip">
<div class="caption">Zulip</div>
<img src="/static/images/story-tutorial/zulip-streams-selected.png" class="zulip-streams-selected" alt="{{ _('Topics in Zulip') }}">
<img src="/static/images/story-tutorial/zulip-streams-selected.png" class="zulip-streams-selected" alt="{{ _('Topics in Zulip') }}" />
</div>
<div class="comparison-slack">
<div class="caption">Other team chat</div>
<img src="/static/images/story-tutorial/slack-streams-selected.png" class="slack-streams-selected" alt="{{ _('Streams in Slack') }}">
<img src="/static/images/story-tutorial/slack-streams-selected.png" class="slack-streams-selected" alt="{{ _('Streams in Slack') }}" />
</div>
</div>
</div>
@ -83,11 +83,11 @@
<div class="zulip-slack-comparison">
<div class="comparison-zulip">
<div class="caption comparison-2-caption-zulip">Zulip</div>
<img src="/static/images/story-tutorial/zulip-streams-unreads-arrows.png" alt="{{ _('Stream topics in Zulip') }}" class="zulip-unreads-arrows">
<img src="/static/images/story-tutorial/zulip-streams-unreads-arrows.png" alt="{{ _('Stream topics in Zulip') }}" class="zulip-unreads-arrows" />
</div>
<div class="comparison-slack">
<div class="caption comparison-2-caption-slack">Other team chat</div>
<img src="/static/images/story-tutorial/slack-streams-unreads.png" class="slack-stream-unreads" alt="{{ _('Streams in Slack') }}">
<img src="/static/images/story-tutorial/slack-streams-unreads.png" class="slack-stream-unreads" alt="{{ _('Streams in Slack') }}" />
</div>
</div>
</div>
@ -95,27 +95,27 @@
<div class="item">
<div class="item-inner">
<p class="tour-item-header">Let&rsquo;s click on &ldquo;Tuesday night catering.&rdquo;</p>
<img src="/static/images/story-tutorial/zulip-topic.png" alt="{{ _('The Tuesday night catering topic in Zulip') }}" class="zulip-topic mobile-hide">
<img src="/static/images/story-tutorial/zulip-topic-crop.png" alt="{{ _('The Tuesday night catering topic in Zulip') }}" class="centered-image zulip-topic mobile-show">
<img src="/static/images/story-tutorial/zulip-topic.png" alt="{{ _('The Tuesday night catering topic in Zulip') }}" class="zulip-topic mobile-hide" />
<img src="/static/images/story-tutorial/zulip-topic-crop.png" alt="{{ _('The Tuesday night catering topic in Zulip') }}" class="centered-image zulip-topic mobile-show" />
</div>
</div>
<div class="item">
<div class="item-inner">
<p class="tour-item-header">Messages in Zulip retain their context even if they&rsquo;re sent hours after the conversation started:</p>
<img src="/static/images/story-tutorial/zulip-streams-easy.png" alt="{{ _('The Tuesday night catering topic in Zulip') }}" class="zulip-easy mobile-hide">
<img src="/static/images/story-tutorial/zulip-streams-easy.png" alt="{{ _('The Tuesday night catering topic in Zulip') }}" class="zulip-easy mobile-hide" />
<div class="mobile-show">
<img src="/static/images/story-tutorial/zulip-streams-easy-mobile-top.png" class="centered-image" alt="{{ _('The Tuesday night catering topic in Zulip') }}">
<img src="/static/images/story-tutorial/zulip-streams-easy-mobile-top.png" class="centered-image" alt="{{ _('The Tuesday night catering topic in Zulip') }}" />
<p class="tour-explanation">Messages sent hours apart are linked in the same topic.</p>
<img src="/static/images/story-tutorial/zulip-streams-easy-mobile-bottom.png" class="centered-image" alt="{{ _('The Tuesday night catering topic in Zulip - compose box') }}">
<img src="/static/images/story-tutorial/zulip-streams-easy-mobile-bottom.png" class="centered-image" alt="{{ _('The Tuesday night catering topic in Zulip - compose box') }}" />
</div>
</div>
</div>
<div class="item">
<div class="item-inner">
<p class="tour-item-header">Without topics, it&rsquo;s hard to catch up efficiently, and hard to participate in conversations that started while you were away.</p>
<img src="/static/images/story-tutorial/slack-overwhelming.png" alt="{{ _('The Tuesday night catering topic in Slack') }}" class="slack-overwhelming mobile-hide">
<img src="/static/images/story-tutorial/slack-overwhelming.png" alt="{{ _('The Tuesday night catering topic in Slack') }}" class="slack-overwhelming mobile-hide" />
<div class="mobile-show">
<img src="/static/images/story-tutorial/slack-overwhelming-mobile.png" class="centered-image" alt="{{ _('The Tuesday night catering topic in Slack') }}">
<img src="/static/images/story-tutorial/slack-overwhelming-mobile.png" class="centered-image" alt="{{ _('The Tuesday night catering topic in Slack') }}" />
<p class="tour-explanation">The last message about Tuesday night catering is hidden 56 messages ago. Meanwhile, you just see a mix of unrelated messages.</p>
</div>
</div>
@ -128,11 +128,11 @@
</a>
<div class="other-resources">
<div class="other-resources-section">
<a href="/why-zulip"><img src="/static/images/landing-page/organised.svg" alt=""></a>
<a href="/why-zulip"><img src="/static/images/landing-page/organised.svg" alt="" /></a>
<p><a href="/why-zulip">Zulip vs Slack &rarr;</a></p>
</div>
<div class="other-resources-section">
<a href="/features"><img src="/static/images/landing-page/featured.svg" alt=""></a>
<a href="/features"><img src="/static/images/landing-page/featured.svg" alt="" /></a>
<p><a href="/features">See all features &rarr;</a></p>
</div>
</div>
@ -169,7 +169,7 @@
Zulip has modern apps for every major platform,
powered by Electron and React Native.
</p>
<br>
<br />
</div>
<div class="platform-icons">
@ -389,49 +389,49 @@
<div class="integration-icons">
<a href="/integrations/doc/travis">
<div class="group">
<img class="integration-logo" src="/static/images/integrations/logos/travis.svg" alt="{{ _('Travis logo') }}">
<img class="integration-logo" src="/static/images/integrations/logos/travis.svg" alt="{{ _('Travis logo') }}" />
<h3 class="integration-name">Travis CI</h3>
<p class="integration-description">See build results immediately</p>
</div>
</a>
<a href="/integrations/doc/github">
<div class="group">
<img class="integration-logo" src="/static/images/integrations/logos/github.svg" alt="{{ _('GitHub logo') }}">
<img class="integration-logo" src="/static/images/integrations/logos/github.svg" alt="{{ _('GitHub logo') }}" />
<h3 class="integration-name">GitHub</h3>
<p class="integration-description">Track issues and pull requests</p>
</div>
</a>
<a href="/integrations/doc/heroku">
<div class="group">
<img class="integration-logo" src="/static/images/integrations/logos/heroku.svg" alt="{{ _('Heroku logo') }}">
<img class="integration-logo" src="/static/images/integrations/logos/heroku.svg" alt="{{ _('Heroku logo') }}" />
<h3 class="integration-name">Heroku</h3>
<p class="integration-description">Keep up with deployments</p>
</div>
</a>
<a href="/integrations/doc/zendesk">
<div class="group">
<img class="integration-logo" src="/static/images/integrations/logos/zendesk.svg" alt="{{ _('Zendesk logo') }}">
<img class="integration-logo" src="/static/images/integrations/logos/zendesk.svg" alt="{{ _('Zendesk logo') }}" />
<h3 class="integration-name">Zendesk</h3>
<p class="integration-description">Receive support tickets and updates</p>
</div>
</a>
<a href="/integrations/doc/jira">
<div class="group">
<img class="integration-logo" src="/static/images/integrations/logos/jira.svg" alt="{{ _('JIRA logo') }}">
<img class="integration-logo" src="/static/images/integrations/logos/jira.svg" alt="{{ _('JIRA logo') }}" />
<h3 class="integration-name">JIRA</h3>
<p class="integration-description">Monitor project bugs and issues</p>
</div>
</a>
<a href="/integrations/doc/sentry">
<div class="group">
<img class="integration-logo" src="/static/images/integrations/logos/sentry.svg" alt="{{ _('Sentry logo') }}">
<img class="integration-logo" src="/static/images/integrations/logos/sentry.svg" alt="{{ _('Sentry logo') }}" />
<h3 class="integration-name">Sentry</h3>
<p class="integration-description">See real-time error tracking</p>
</div>
</a>
<a href="/integrations/doc/pagerduty" class="hide-1">
<div class="group">
<img class="integration-logo" src="/static/images/integrations/logos/pagerduty.svg" alt="{{ _('Pagerduty logo') }}">
<img class="integration-logo" src="/static/images/integrations/logos/pagerduty.svg" alt="{{ _('Pagerduty logo') }}" />
<h3 class="integration-name">Pagerduty</h3>
<p class="integration-description">Connect to your monitoring systems</p>
</div>
@ -501,7 +501,7 @@
</div>
</div>
<hr>
<hr />
<div class="company-container">
<header>

View File

@ -18,7 +18,7 @@ Markdown
HTML
```
<a href="https://chat.zulip.org"><img src="https://img.shields.io/badge/zulip-join_chat-brightgreen.svg"></a>
<a href="https://chat.zulip.org"><img src="https://img.shields.io/badge/zulip-join_chat-brightgreen.svg" /></a>
```
## Link to a stream or topic

View File

@ -6,7 +6,7 @@
{% endblock %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}

View File

@ -2,7 +2,7 @@
{% set entrypoint = "integrations" %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block hello_page_container %} hello-main{% endblock %}
@ -85,7 +85,7 @@
</h4>
</a>
{% endfor %}
<hr>
<hr />
<h3>{% trans %}Custom integrations{% endtrans %}</h3>
<a href="/api/incoming-webhooks-overview">
<h4>{% trans %}Incoming webhooks{% endtrans %}</h4>
@ -125,7 +125,7 @@
</a>
{% endif %}
{% endfor %}
<hr>
<hr />
<div class="integration-request center">
<p>Don't see an integration you need? We'd love to help.</p>
<a href="/api/integrations-overview" class="button green">

View File

@ -9,7 +9,7 @@
<div class="app-main white-box">
{{ _('Hi there! Thank you for your interest in Zulip.') }}
<br>
<br />
{% trans %}There is no Zulip organization hosted at this subdomain.{% endtrans %}
</div>
</div>

View File

@ -42,7 +42,7 @@ page can be easily identified in it's respective JavaScript file. -->
{% if password_auth_enabled %}
<form name="login_form" id="login_form" method="post" class="login-form"
action="{{ url('login') }}">
<input type="hidden" name="next" value="{{ next }}">
<input type="hidden" name="next" value="{{ next }}" />
{% if two_factor_authentication_enabled %}
{{ wizard.management_form }}
@ -112,7 +112,7 @@ page can be easily identified in it's respective JavaScript file. -->
{% for backend in page_params.external_authentication_methods %}
<div class="login-social">
<form class="social_login_form form-inline" action="{{ backend.login_url }}" method="get">
<input type="hidden" name="next" value="{{ next }}">
<input type="hidden" name="next" value="{{ next }}" />
<button id="login_{{ backend.button_id_suffix }}" class="login-social-button"
{% if backend.display_icon %} style="background-image:url({{ backend.display_icon }})" {% endif %}> {{ _('Log in with %(identity_provider)s', identity_provider=backend.display_name) }}
</button>

View File

@ -1,28 +1,28 @@
<!-- Google / Search Engine Tags -->
{% if allow_search_engine_indexing %}
{% if OPEN_GRAPH_DESCRIPTION %}
<meta name="description" content="{{ OPEN_GRAPH_DESCRIPTION }}">
<meta name="description" content="{{ OPEN_GRAPH_DESCRIPTION }}" />
{% else %}
<meta name="description" content="Zulip combines the immediacy of real-time chat with an email threading model. With Zulip, you can catch up on important conversations while ignoring irrelevant ones.">
<meta name="description" content="Zulip combines the immediacy of real-time chat with an email threading model. With Zulip, you can catch up on important conversations while ignoring irrelevant ones." />
{% endif %}
{% else %}
<meta name="robots" content="noindex,nofollow">
<meta name="robots" content="noindex,nofollow" />
{% endif %}
<!-- Open Graph / Facebook / Twitter Meta Tags -->
<meta property="og:url" content="{{ OPEN_GRAPH_URL }}">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Zulip">
<meta property="og:url" content="{{ OPEN_GRAPH_URL }}" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Zulip" />
{% if OPEN_GRAPH_TITLE %}
<meta property="og:title" content="{{ OPEN_GRAPH_TITLE }}">
<meta property="og:description" content="{{ OPEN_GRAPH_DESCRIPTION }}">
<meta property="og:title" content="{{ OPEN_GRAPH_TITLE }}" />
<meta property="og:description" content="{{ OPEN_GRAPH_DESCRIPTION }}" />
{% else %}
<meta property="og:title" content="Chat for distributed teams">
<meta property="og:description" content="Zulip combines the immediacy of real-time chat with an email threading model. With Zulip, you can catch up on important conversations while ignoring irrelevant ones.">
<meta property="og:title" content="Chat for distributed teams" />
<meta property="og:description" content="Zulip combines the immediacy of real-time chat with an email threading model. With Zulip, you can catch up on important conversations while ignoring irrelevant ones." />
{% endif %}
{% if OPEN_GRAPH_IMAGE %}
<meta property="og:image" content="{{ OPEN_GRAPH_IMAGE }}">
<meta property="og:image" content="{{ OPEN_GRAPH_IMAGE }}" />
{% else %}
<meta property="og:image" content="{{ realm_uri }}/static/images/logo/zulip-icon-128x128.png">
<meta property="og:image" content="{{ realm_uri }}/static/images/logo/zulip-icon-128x128.png" />
{% endif %}
<meta name="twitter:card" content="summary">
<meta name="twitter:card" content="summary" />

View File

@ -2,7 +2,7 @@
{% set entrypoint = "landing-page" %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}
@ -28,7 +28,7 @@
<div class="description">
Try Zulip for an unlimited period of time.
</div>
<hr>
<hr />
<ul class="feature-list">
<li>10,000 messages of search history</li>
<li>File storage up to 5 GB total</li>
@ -61,7 +61,7 @@
<div class="description">
Make Zulip your home.
</div>
<hr>
<hr />
<ul class="feature-list">
<li>Full search history</li>
<li>File storage up to 10 GB per user</li>
@ -77,7 +77,7 @@
<div class="price">6<span class="price-cents">.67</span></div>
<div class="details">
per user per month
<br>
<br />
$8/month billed monthly
</div>
</div>
@ -122,7 +122,7 @@
<div class="description">
Install your own Zulip server.
</div>
<hr>
<hr />
<ul class="feature-list">
<li>Easy self-service installation</li>
<li>Free and open source forever under Apache 2.0 license</li>
@ -148,7 +148,7 @@
<div class="description">
For mission-critical installations.
</div>
<hr>
<hr />
<ul class="feature-list">
<li>Easy self-service installation</li>
<li>Support response SLAs</li>

View File

@ -6,7 +6,7 @@
{% endblock %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}

View File

@ -2,7 +2,7 @@
{% set entrypoint = "landing-page" %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}

View File

@ -180,7 +180,7 @@ Form is validated both client-side using jquery-validate (see signup.js) and ser
{% endif %}
</fieldset>
{% if default_stream_groups %}
<hr>
<hr />
<div class="default-stream-groups">
<p class="margin">{{ _('What are you interested in?') }}</p>
{% for default_stream_group in default_stream_groups %}
@ -206,7 +206,7 @@ Form is validated both client-side using jquery-validate (see signup.js) and ser
</div>
{% endfor %}
</div>
<hr>
<hr />
{% endif %}
<div class="input-group margin terms-of-service">

View File

@ -6,7 +6,7 @@
{% endblock %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}

View File

@ -86,28 +86,28 @@
</p>
<div class="contributors-list">
<input id="total" type="radio" name="tabs" checked>
<input id="total" type="radio" name="tabs" checked />
<label for="total"><i class="fa fa-globe" aria-hidden="true"></i>&nbsp; Total</label>
<input id="server" type="radio" name="tabs">
<input id="server" type="radio" name="tabs" />
<label for="server"><i class="fa fa-server" aria-hidden="true"></i>&nbsp; Server</label>
<input id="desktop" type="radio" name="tabs">
<input id="desktop" type="radio" name="tabs" />
<label for="desktop"><i class="fa fa-desktop" aria-hidden="true"></i>&nbsp; Desktop</label>
<input id="mobile" type="radio" name="tabs">
<input id="mobile" type="radio" name="tabs" />
<label for="mobile"><i class="fa fa-mobile" aria-hidden="true"></i>&nbsp; Mobile</label>
<input id="python-zulip-api" type="radio" name="tabs">
<input id="python-zulip-api" type="radio" name="tabs" />
<label for="python-zulip-api"><i class="fa fa-code" aria-hidden="true"></i>&nbsp; Python API</label>
<input id="zulip-js" type="radio" name="tabs">
<input id="zulip-js" type="radio" name="tabs" />
<label for="zulip-js"><i class="fa fa-code" aria-hidden="true"></i>&nbsp; JS API</label>
<input id="zulipbot" type="radio" name="tabs">
<input id="zulipbot" type="radio" name="tabs" />
<label for="zulipbot"><i class="fa fa-at" aria-hidden="true"></i>&nbsp; Zulipbot</label>
<input id="terminal" type="radio" name="tabs">
<input id="terminal" type="radio" name="tabs" />
<label for="terminal"><i class="fa fa-terminal" aria-hidden="true"></i>&nbsp; Terminal</label>
<div id="tab-total" class="contributors"></div>
@ -128,7 +128,7 @@
<img class="avatar_img" src="<%= avatar %>" alt="{{ _('Avatar') }}" />
</div>
<div class='info'>
<b><%= name %></b><br>
<b><%= name %></b><br />
<%= commits %> <%= commits === 1 ? 'commit' : 'commits' %>
</div>
</a>

View File

@ -4,7 +4,7 @@
{# Terms of Service. #}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}

View File

@ -9,7 +9,7 @@
{% endblock %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block portico_content %}

View File

@ -8,7 +8,7 @@ Mattermost, Discord, Spark, and others.
Anyone who wakes up to this frequently can tell you it is not fun.
<img src="/static/images/why-zulip/slack-unreads.png" class="slack-image" alt="Slack unreads">
<img src="/static/images/why-zulip/slack-unreads.png" class="slack-image" alt="Slack unreads" />
The lack of organization and context in Slack channels means that anyone
using Slack heavily has to manually scan through hundreds of messages a day
@ -124,13 +124,13 @@ effective threading model: Every channel message has a topic, just
like every message in email has a subject line. (Channels are called
streams in Zulip.)
<img src="/static/images/why-zulip/zulip-topics.png" class="zulip-topics-image" alt="Zulip topics">
<img src="/static/images/why-zulip/zulip-topics.png" class="zulip-topics-image" alt="Zulip topics" />
Topics hold Zulip conversations together, just like subject lines hold email
conversations together. They allow you to efficiently catch up on messages
and reply in context, even to conversations that started hours or days ago.
<img src="/static/images/why-zulip/zulip-reply-later.png" class="zulip-reply-later-image" alt="Zulip reply later">
<img src="/static/images/why-zulip/zulip-reply-later.png" class="zulip-reply-later-image" alt="Zulip reply later" />
## Zulip changes how you can operate.

View File

@ -120,9 +120,9 @@ def tokenize(text: str) -> List[Token]:
tag = tag_parts[0]
if is_special_html_tag(s, tag):
kind = "html_special"
elif is_self_closing_html_tag(s, tag):
if tag == "!DOCTYPE":
kind = "html_doctype"
elif s.endswith("/>"):
kind = "html_singleton"
else:
kind = "html_start"
@ -203,6 +203,26 @@ def tokenize(text: str) -> List[Token]:
return tokens
HTML_VOID_TAGS = {
"area",
"base",
"br",
"col",
"command",
"embed",
"hr",
"img",
"input",
"keygen",
"link",
"meta",
"param",
"source",
"track",
"wbr",
}
def validate(
fn: Optional[str] = None, text: Optional[str] = None, check_indent: bool = True
) -> None:
@ -227,6 +247,7 @@ def validate(
class State:
def __init__(self, func: Callable[[Token], None]) -> None:
self.depth = 0
self.foreign = False
self.matcher = func
def no_start_tag(token: Token) -> None:
@ -249,6 +270,10 @@ def validate(
start_col = start_token.col
old_matcher = state.matcher
old_foreign = state.foreign
if start_tag in ["math", "svg"]:
state.foreign = True
def f(end_token: Token) -> None:
@ -283,6 +308,7 @@ def validate(
"""
)
state.matcher = old_matcher
state.foreign = old_foreign
state.depth -= 1
state.matcher = f
@ -292,7 +318,16 @@ def validate(
tag = token.tag
if kind == "html_start":
if not state.foreign and tag in HTML_VOID_TAGS:
raise TemplateParserException(
f"Tag must be self-closing: {tag} at {fn} line {token.line}, col {token.col}"
)
start_tag_matcher(token)
elif kind == "html_singleton":
if not state.foreign and tag not in HTML_VOID_TAGS:
raise TemplateParserException(
f"Tag must not be self-closing: {tag} at {fn} line {token.line}, col {token.col}"
)
elif kind == "html_end":
state.matcher(token)
@ -315,45 +350,6 @@ def validate(
raise TemplateParserException("Missing end tag")
def is_special_html_tag(s: str, tag: str) -> bool:
return tag in ["link", "meta", "!DOCTYPE"]
OPTIONAL_CLOSING_TAGS = [
"br",
"circle",
"img",
"input",
"path",
"polygon",
"stop",
]
def is_self_closing_html_tag(s: str, tag: str) -> bool:
if s.endswith("/>"):
if tag in OPTIONAL_CLOSING_TAGS:
return True
raise TokenizationException("Singleton tag not allowed", tag)
self_closing_tag = tag in [
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"param",
"source",
"track",
"wbr",
]
if self_closing_tag:
return True
return False
def is_django_block_tag(tag: str) -> bool:
return tag in [
"autoescape",

View File

@ -3,6 +3,7 @@ import unittest
import tools.lib.template_parser
from tools.lib.html_branches import (
Node,
build_id_dict,
get_tag_info,
html_branches,
@ -49,35 +50,40 @@ class TestHtmlBranches(unittest.TestCase):
tree = html_tag_tree(html)
assert tree.children[0].token is not None
self.assertEqual(tree.children[0].token.kind, "html_start")
self.assertEqual(tree.children[0].token.tag, "html")
assert tree.children[0].children[0].token is not None
self.assertEqual(tree.children[0].children[0].token.kind, "html_start")
self.assertEqual(tree.children[0].children[0].token.tag, "head")
assert tree.children[0].children[0].children[0].token is not None
self.assertEqual(tree.children[0].children[0].children[0].token.kind, "html_start")
self.assertEqual(tree.children[0].children[0].children[0].token.tag, "title")
assert tree.children[0].children[1].token is not None
self.assertEqual(tree.children[0].children[1].token.kind, "html_start")
self.assertEqual(tree.children[0].children[1].token.tag, "body")
assert tree.children[0].children[1].children[0].token is not None
self.assertEqual(tree.children[0].children[1].children[0].token.kind, "html_start")
self.assertEqual(tree.children[0].children[1].children[0].token.tag, "p")
assert tree.children[0].children[1].children[0].children[0].token is not None
self.assertEqual(
tree.children[0].children[1].children[0].children[0].token.kind, "html_singleton"
def serialize(node: Node) -> object:
return (
node.token and (node.token.kind, node.token.tag),
[serialize(child) for child in node.children],
)
self.assertEqual(tree.children[0].children[1].children[0].children[0].token.tag, "br")
assert tree.children[0].children[1].children[1].token is not None
self.assertEqual(tree.children[0].children[1].children[1].token.kind, "html_start")
self.assertEqual(tree.children[0].children[1].children[1].token.tag, "p")
expected = (
None,
[
(
("html_start", "html"),
[
(
("html_start", "head"),
[
(("html_start", "title"), []),
(("html_singleton", "meta"), []),
(("html_singleton", "link"), []),
],
),
(
("html_start", "body"),
[
(
("html_start", "p"),
[(("html_start", "br"), []), (("html_start", "p"), [])],
)
],
),
],
)
],
)
self.assertEqual(serialize(tree), expected)
def test_html_branches(self) -> None:
html = """
@ -99,19 +105,16 @@ class TestHtmlBranches(unittest.TestCase):
"""
branches = html_branches(html)
self.assertEqual(branches[0].text(), "html head title")
self.assertEqual(branches[1].text(), "html body p br")
self.assertEqual(branches[2].text(), "html body p")
self.assertEqual(
branches[0].staircase_text(), "\n html\n head\n title\n"
[(branch.text(), branch.staircase_text()) for branch in branches],
[
("html head title", "\n html\n head\n title\n"),
("html head meta", "\n html\n head\n meta\n"),
("html head link", "\n html\n head\n link\n"),
("html body p br", "\n html\n body\n p\n br\n"),
("html body p p", "\n html\n body\n p\n p\n"),
],
)
self.assertEqual(
branches[1].staircase_text(),
"\n html\n body\n p\n br\n",
)
self.assertEqual(branches[2].staircase_text(), "\n html\n body\n p\n")
def test_build_id_dict(self) -> None:
templates = ["test_template1.html", "test_template2.html"]

View File

@ -20,7 +20,7 @@ BAD_HTML = """
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div><p>Hello<br>world!</p></div>
<div><p>Hello<br />world!</p></div>
<p>Goodbye<!-- test -->world!</p>
<table>
<tr>
@ -52,7 +52,7 @@ GOOD_HTML = """
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div><p>Hello<br>world!</p></div>
<div><p>Hello<br />world!</p></div>
<p>Goodbye<!-- test -->world!</p>
<table>
<tr>
@ -338,7 +338,7 @@ BAD_HTML13 = """
{{#if this.is_realm_emoji}}
<img src="{{this.url}}" class="emoji" />
{{else}}
<br>
<br />
{{/if}}
{{/if}}
<div>{{this.count}}</div>
@ -353,7 +353,7 @@ GOOD_HTML13 = """
{{#if this.is_realm_emoji}}
<img src="{{this.url}}" class="emoji" />
{{else}}
<br>
<br />
{{/if}}
{{/if}}
<div>{{this.count}}</div>

View File

@ -260,23 +260,23 @@ class ParserTest(unittest.TestCase):
validate(text=my_html)
def test_tokenize(self) -> None:
tag = "<meta whatever>bla"
tag = "<!DOCTYPE html>"
token = tokenize(tag)[0]
self.assertEqual(token.kind, "html_special")
self.assertEqual(token.kind, "html_doctype")
tag = "<a>bla"
token = tokenize(tag)[0]
self.assertEqual(token.kind, "html_start")
self.assertEqual(token.tag, "a")
tag = "<br>bla"
tag = "<br />bla"
token = tokenize(tag)[0]
self.assertEqual(token.kind, "html_singleton")
self.assertEqual(token.tag, "br")
tag = "<input>bla"
token = tokenize(tag)[0]
self.assertEqual(token.kind, "html_singleton")
self.assertEqual(token.kind, "html_start") # We later mark this an error.
self.assertEqual(token.tag, "input")
tag = "<input />bla"

View File

@ -45,4 +45,4 @@ API_FEATURE_LEVEL = 57
# historical commits sharing the same major version, in which case a
# minor version bump suffices.
PROVISION_VERSION = "141.1"
PROVISION_VERSION = "141.2"

View File

@ -54,7 +54,7 @@ class DocPageTest(ZulipTestCase):
self.assertIn(s, str(result.content))
if not doc_html_str:
self.assert_in_success_response(
['<meta name="robots" content="noindex,nofollow">'], result
['<meta name="robots" content="noindex,nofollow" />'], result
)
# Test the URL on the root subdomain
@ -65,7 +65,7 @@ class DocPageTest(ZulipTestCase):
self.assertIn(expected_content, str(result.content))
if not doc_html_str:
self.assert_in_success_response(
['<meta name="robots" content="noindex,nofollow">'], result
['<meta name="robots" content="noindex,nofollow" />'], result
)
for s in extra_strings:
@ -88,7 +88,7 @@ class DocPageTest(ZulipTestCase):
# Every page has a meta-description
self.assert_in_success_response(['<meta name="description" content="'], result)
self.assert_not_in_success_response(
['<meta name="robots" content="noindex,nofollow">'], result
['<meta name="robots" content="noindex,nofollow" />'], result
)
# Test the URL on the "zephyr" subdomain with the landing page setting
@ -101,7 +101,7 @@ class DocPageTest(ZulipTestCase):
self.assertIn(s, str(result.content))
if not doc_html_str:
self.assert_in_success_response(
['<meta name="robots" content="noindex,nofollow">'], result
['<meta name="robots" content="noindex,nofollow" />'], result
)
def test_api_doc_endpoints(self) -> None:
@ -172,14 +172,14 @@ class DocPageTest(ZulipTestCase):
def test_portico_pages_open_graph_metadata(self) -> None:
# Why Zulip
url = "/why-zulip/"
title = '<meta property="og:title" content="Team chat with first-class threading">'
title = '<meta property="og:title" content="Team chat with first-class threading" />'
description = '<meta property="og:description" content="Most team chats are overwhelming'
self._test(url, title, doc_html_str=True)
self._test(url, description, doc_html_str=True)
# Features
url = "/features/"
title = '<meta property="og:title" content="Zulip Features">'
title = '<meta property="og:title" content="Zulip Features" />'
description = '<meta property="og:description" content="First class threading'
self._test(url, title, doc_html_str=True)
self._test(url, description, doc_html_str=True)
@ -202,21 +202,21 @@ class DocPageTest(ZulipTestCase):
def test_integration_pages_open_graph_metadata(self) -> None:
url = "/integrations/doc/github"
title = '<meta property="og:title" content="Connect GitHub to Zulip">'
title = '<meta property="og:title" content="Connect GitHub to Zulip" />'
description = '<meta property="og:description" content="Zulip comes with over'
self._test(url, title, doc_html_str=True)
self._test(url, description, doc_html_str=True)
# Test category pages
url = "/integrations/communication"
title = '<meta property="og:title" content="Connect your Communication tools to Zulip">'
title = '<meta property="og:title" content="Connect your Communication tools to Zulip" />'
description = '<meta property="og:description" content="Zulip comes with over'
self._test(url, title, doc_html_str=True)
self._test(url, description, doc_html_str=True)
# Test integrations page
url = "/integrations/"
title = '<meta property="og:title" content="Connect the tools you use to Zulip">'
title = '<meta property="og:title" content="Connect the tools you use to Zulip" />'
description = '<meta property="og:description" content="Zulip comes with over'
self._test(url, title, doc_html_str=True)
self._test(url, description, doc_html_str=True)