docs: Capitalize Markdown consistently.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2020-08-10 16:47:49 -07:00 committed by Tim Abbott
parent 60a25b2721
commit 768f9f93cd
76 changed files with 200 additions and 199 deletions

View File

@ -1,6 +1,6 @@
# Zulip markdown documentation hosted elsewhere # Zulip Markdown documentation hosted elsewhere
The markdown files in this directory ( /zulip/docs ) are not intended The Markdown files in this directory ( /zulip/docs ) are not intended
to be read on GitHub. Instead, visit our to be read on GitHub. Instead, visit our
[ReadTheDocs](https://zulip.readthedocs.io/en/latest/index.html) to [ReadTheDocs](https://zulip.readthedocs.io/en/latest/index.html) to
read the Zulip documentation. read the Zulip documentation.

View File

@ -71,7 +71,7 @@ process.
For many of our project ideas, you'll be working inside a Zulip For many of our project ideas, you'll be working inside a Zulip
development environment (because the documentation is implemented as development environment (because the documentation is implemented as
markdown in the main Zulip repository, and can be previewed using Markdown in the main Zulip repository, and can be previewed using
tools in the Zulip development environment). See tools in the Zulip development environment). See
[our documentation on documentation systems](../documentation/overview.md) [our documentation on documentation systems](../documentation/overview.md)
for details on our various existing documentation systems. for details on our various existing documentation systems.
@ -188,7 +188,7 @@ started.).
Once you have several PRs merged (or at least one significant PR Once you have several PRs merged (or at least one significant PR
merged, or the equivalent of this for projects that don't involve merged, or the equivalent of this for projects that don't involve
writing markdown code), you can start discussing with the Zulip writing Markdown code), you can start discussing with the Zulip
development community the project you'd like to do, and develop a development community the project you'd like to do, and develop a
specific project plan. We recommend discussing what you're thinking specific project plan. We recommend discussing what you're thinking
in public streams on chat.zulip.org, so it's easy to get quick in public streams on chat.zulip.org, so it's easy to get quick
@ -271,7 +271,7 @@ most cases, our documentation explains how to accomplish a task in the
Zulip webapp, but doesn't cover how to do those tasks in Zulip's Zulip webapp, but doesn't cover how to do those tasks in Zulip's
mobile and beta terminal apps. mobile and beta terminal apps.
We have recently built a nice markdown-based system to make it easy to show We have recently built a nice Markdown-based system to make it easy to show
information for multiple platforms in a single tabbed widget. An example information for multiple platforms in a single tabbed widget. An example
article using this widget is article using this widget is
[logging in](https://zulip.com/help/logging-in). This project will [logging in](https://zulip.com/help/logging-in). This project will

View File

@ -30,7 +30,7 @@ Our API documentation is defined by a few sets of files:
[OpenAPI configuration](../documentation/openapi.md) at [OpenAPI configuration](../documentation/openapi.md) at
`zerver/openapi/zulip.yaml`. `zerver/openapi/zulip.yaml`.
* The top-level templates live under `templates/zerver/api/*`, and are * The top-level templates live under `templates/zerver/api/*`, and are
written using the markdown framework that powers our [user written using the Markdown framework that powers our [user
docs](../documentation/user.md), with some special extensions for docs](../documentation/user.md), with some special extensions for
rendering nice code blocks and example responses. We expect to rendering nice code blocks and example responses. We expect to
eventually remove most of these files where it is possible to eventually remove most of these files where it is possible to
@ -268,16 +268,16 @@ above.
comments. The lines inside these comments are what will be displayed as the comments. The lines inside these comments are what will be displayed as the
code example on our `/api` page. code example on our `/api` page.
1. Finally, write the markdown file for your API endpoint under 1. Finally, write the Markdown file for your API endpoint under
`templates/zerver/api/`. This is usually pretty easy to template `templates/zerver/api/`. This is usually pretty easy to template
off existing endpoints; but refer to the system explanations above off existing endpoints; but refer to the system explanations above
for details. for details.
1. Add the markdown file to the index in `templates/zerver/help/include/rest-endpoints.md`. 1. Add the Markdown file to the index in `templates/zerver/help/include/rest-endpoints.md`.
1. Test your endpoint, pretending to be a new user in a hurry, by 1. Test your endpoint, pretending to be a new user in a hurry, by
visiting it via the links on `http://localhost:9991/api` (the API visiting it via the links on `http://localhost:9991/api` (the API
docs are rendered from the markdown source files on page load, so docs are rendered from the Markdown source files on page load, so
just reload to see an updated version as you edit). You should just reload to see an updated version as you edit). You should
make sure that copy-pasting the code in your examples works, and make sure that copy-pasting the code in your examples works, and
post an example of the output in the pull request. post an example of the output in the pull request.
@ -287,7 +287,7 @@ above.
## Why a custom system? ## Why a custom system?
Given that our documentation is written in large part using the Given that our documentation is written in large part using the
OpenAPI format, why maintain a custom markdown system for displaying OpenAPI format, why maintain a custom Markdown system for displaying
it? There's several major benefits to this system: it? There's several major benefits to this system:
* It is extremely common for API documentation to become out of date * It is extremely common for API documentation to become out of date

View File

@ -39,7 +39,7 @@ form `/help/foo`; with special cases for `/help/` going to `index.md` and
are usually linked from `static/images/help/`. are usually linked from `static/images/help/`.
This means that you can contribute to the Zulip user documentation by just This means that you can contribute to the Zulip user documentation by just
adding to or editing the collection of markdown files under adding to or editing the collection of Markdown files under
`templates/zerver/help`. If you have the Zulip development environment `templates/zerver/help`. If you have the Zulip development environment
setup, you simply need to reload your browser on setup, you simply need to reload your browser on
`http://localhost:9991/help/foo` to see the latest version of `foo.md` `http://localhost:9991/help/foo` to see the latest version of `foo.md`
@ -100,7 +100,7 @@ your documentation to help improve its readability:
* Since raw HTML is supported in Markdown, you can include arbitrary * Since raw HTML is supported in Markdown, you can include arbitrary
HTML/CSS in your documentation as needed. HTML/CSS in your documentation as needed.
* Code blocks allow you to highlight syntax, similar to Zulip's own markdown. * Code blocks allow you to highlight syntax, similar to Zulip's own Markdown.
* Anchor tags can be used to link to headers in other documents. * Anchor tags can be used to link to headers in other documents.
* [Images](#images) of Zulip UI can be added to documentation. * [Images](#images) of Zulip UI can be added to documentation.
* Inline [icons](#icons) used to refer to features in the Zulip UI. * Inline [icons](#icons) used to refer to features in the Zulip UI.
@ -108,7 +108,7 @@ HTML/CSS in your documentation as needed.
documentation. documentation.
* You can create special highlight warning blocks using * You can create special highlight warning blocks using
[tips and warnings](#tips-and-warnings). [tips and warnings](#tips-and-warnings).
* You can create tabs using [markdown tab switcher](#tab-switcher). * You can create tabs using [Markdown tab switcher](#tab-switcher).
### Images ### Images
@ -232,7 +232,7 @@ should be formatted as a continuation of a numbered step.
### Tab Switcher ### Tab Switcher
Our markdown processor supports easily creating a tab switcher widget Our Markdown processor supports easily creating a tab switcher widget
design to easily show the instructions for different design to easily show the instructions for different
[platforms](https://zulip.com/help/logging-out) in user docs, [platforms](https://zulip.com/help/logging-out) in user docs,
languages in API docs, etc. To create a tab switcher, write: languages in API docs, etc. To create a tab switcher, write:

View File

@ -111,9 +111,9 @@ in bursts.
- Added documentation for several more API endpoints. - Added documentation for several more API endpoints.
- Added new email address visibility option hiding real email - Added new email address visibility option hiding real email
addresses from organization administrators in the Zulip UI. addresses from organization administrators in the Zulip UI.
- Added new "Mention time" markdown feature to communicate about times - Added new "Mention time" Markdown feature to communicate about times
in a timezone-aware fashion. in a timezone-aware fashion.
- Added new "Spoiler" markdown feature to hide text until interaction. - Added new "Spoiler" Markdown feature to hide text until interaction.
- Added a new API that allows the mobile/desktop/terminal apps to - Added a new API that allows the mobile/desktop/terminal apps to
open uploaded files in an external browser that may not be logged in. open uploaded files in an external browser that may not be logged in.
- Added several database indexes that significantly improve - Added several database indexes that significantly improve
@ -164,7 +164,7 @@ in bursts.
- Improved UI for picking which streams to invite new users to. - Improved UI for picking which streams to invite new users to.
- Improved UI for reviewing one's muted topics. - Improved UI for reviewing one's muted topics.
- Improved UI for message edit history. - Improved UI for message edit history.
- Fixed many minor issues with Zulip's markdown processors. - Fixed many minor issues with Zulip's Markdown processors.
- Fixed many subtle issues with the message editing UI. - Fixed many subtle issues with the message editing UI.
- Fixed several subtle issues with the default nginx configuration. - Fixed several subtle issues with the default nginx configuration.
- Fixed minor issues with various keyboard shortcuts. - Fixed minor issues with various keyboard shortcuts.
@ -222,7 +222,7 @@ in bursts.
settings system to be more maintainable. settings system to be more maintainable.
- This release largely completes the SCSS refactoring of the codebase. - This release largely completes the SCSS refactoring of the codebase.
- Replaced our CasperJS frontend integration test system with Puppeteer. - Replaced our CasperJS frontend integration test system with Puppeteer.
- Extracted the typeahead and markdown libraries for reuse in the - Extracted the typeahead and Markdown libraries for reuse in the
mobile apps. mobile apps.
- Removed the legacy websockets-based system for sending messages. This - Removed the legacy websockets-based system for sending messages. This
system was always a hack, was only ever used for one endpoint, and system was always a hack, was only ever used for one endpoint, and
@ -372,7 +372,7 @@ details.
to existing Slack/HipChat/Gitter import tools). Slack import now to existing Slack/HipChat/Gitter import tools). Slack import now
supports importing data only included in corporate exports, supports importing data only included in corporate exports,
including private messages and shared channels. including private messages and shared channels.
- Added markdown support and typeahead for mentioning topics. - Added Markdown support and typeahead for mentioning topics.
- Email notifications have been completely redesigned with a minimal, - Email notifications have been completely redesigned with a minimal,
readable style inspired by GitHub's email notifications. readable style inspired by GitHub's email notifications.
- We merged significant preparatory work for supporting RHEL/CentOS in - We merged significant preparatory work for supporting RHEL/CentOS in
@ -468,7 +468,7 @@ lose the setting and need to re-enable it.
- Added a new setting to control whether inactive streams are demoted. - Added a new setting to control whether inactive streams are demoted.
- Added webapp support for new desktop app features: inline reply - Added webapp support for new desktop app features: inline reply
from notifications, and detecting user presence from OS APIs. from notifications, and detecting user presence from OS APIs.
- Added markdown support for headings, implemented using `# heading`, - Added Markdown support for headings, implemented using `# heading`,
and removed several other unnecessary differences from CommonMark. and removed several other unnecessary differences from CommonMark.
- Added local echo when editing messages for a more responsive experience. - Added local echo when editing messages for a more responsive experience.
- Changes to global notification settings for stream messages now - Changes to global notification settings for stream messages now
@ -497,7 +497,7 @@ lose the setting and need to re-enable it.
- The administrative UI for managing bots now nicely links to the - The administrative UI for managing bots now nicely links to the
bot's owner. bot's owner.
- Restructured "private messages" widget to have a cleaner design. - Restructured "private messages" widget to have a cleaner design.
- Significantly improved performance of the backend markdown processor. - Significantly improved performance of the backend Markdown processor.
- Significantly improved Help Center documentation of dozens of features. - Significantly improved Help Center documentation of dozens of features.
- Simplified and internationalized some notification bot messages. - Simplified and internationalized some notification bot messages.
- The compose box placeholder now shows users active status. - The compose box placeholder now shows users active status.
@ -507,7 +507,7 @@ lose the setting and need to re-enable it.
- Improved default nginx TLS settings for stronger security. - Improved default nginx TLS settings for stronger security.
- Improved UI of administrative user management UI. - Improved UI of administrative user management UI.
- Improved error messages for various classes of invalid searches. - Improved error messages for various classes of invalid searches.
- Improved styling of both markdown unordered and numbered lists. - Improved styling of both Markdown unordered and numbered lists.
- Compose typeahead now autofills stream field if only subscribed to - Compose typeahead now autofills stream field if only subscribed to
one stream. one stream.
- Bot users can now post to announcement-only streams if their owners - Bot users can now post to announcement-only streams if their owners
@ -552,7 +552,7 @@ lose the setting and need to re-enable it.
sent to a private stream with shared history before the current user sent to a private stream with shared history before the current user
joined that stream. joined that stream.
- Fixed several subtle real-time sync issues with "stream settings". - Fixed several subtle real-time sync issues with "stream settings".
- Fixed a few subtle markdown processor bugs involving emoji. - Fixed a few subtle Markdown processor bugs involving emoji.
- Fixed several issues where Linkifiers validation was overly restrictive. - Fixed several issues where Linkifiers validation was overly restrictive.
- Fixed several rare/minor UI consistency issues in the left sidebar. - Fixed several rare/minor UI consistency issues in the left sidebar.
- Fixed issues involving saving a message edit before file upload completes. - Fixed issues involving saving a message edit before file upload completes.
@ -712,7 +712,7 @@ and is enabled by default in that case. To disable it, set
- Added internationalization for outgoing emails. - Added internationalization for outgoing emails.
- Added a ReviewBoard integration, and improved numerous existing integrations. - Added a ReviewBoard integration, and improved numerous existing integrations.
- Added support for multi-line messages for the /me feature. - Added support for multi-line messages for the /me feature.
- Added markdown rendering of text when displaying custom profile fields. - Added Markdown rendering of text when displaying custom profile fields.
- Added "silent mentions" syntax (`@_**Tim Abbott**`), which show - Added "silent mentions" syntax (`@_**Tim Abbott**`), which show
visually, but don't trigger a notification to the target user. visually, but don't trigger a notification to the target user.
- Added support for using lightbox in compose preview. - Added support for using lightbox in compose preview.
@ -720,7 +720,7 @@ and is enabled by default in that case. To disable it, set
fixes a common source of confusion for new users. fixes a common source of confusion for new users.
- Suppressed notifications when quoting a message mentioning yourself. - Suppressed notifications when quoting a message mentioning yourself.
- Message editing now has the compose widgets for emoji, video calls, etc. - Message editing now has the compose widgets for emoji, video calls, etc.
- Message editing now has a markdown preview feature just like compose. - Message editing now has a Markdown preview feature just like compose.
- Message editing now uses same "enter-sends" behavior as compose. - Message editing now uses same "enter-sends" behavior as compose.
- Organization administrators can now edit users' custom profile fields. - Organization administrators can now edit users' custom profile fields.
- Optimized performance of data import from Slack, HipChat, etc. - Optimized performance of data import from Slack, HipChat, etc.
@ -973,7 +973,7 @@ Zulip installations; it has minimal changes for existing servers.
- Added new ctrl+B, ctrl+I, ctrl+L compose shortcuts for inserting - Added new ctrl+B, ctrl+I, ctrl+L compose shortcuts for inserting
common syntax. common syntax.
- Added warning when linking to a private stream via typeahead. - Added warning when linking to a private stream via typeahead.
- Added support for automatically-numbered markdown lists. - Added support for automatically-numbered Markdown lists.
- Added a big warning when posting to #announce. - Added a big warning when posting to #announce.
- Added a notification when drafts are saved, to make them more - Added a notification when drafts are saved, to make them more
discoverable. discoverable.
@ -1014,7 +1014,7 @@ Zulip installations; it has minimal changes for existing servers.
by last modification, not creation. by last modification, not creation.
- Removed the legacy "Zulip labs" autoscroll_forever setting. It was - Removed the legacy "Zulip labs" autoscroll_forever setting. It was
enabled mostly by accident. enabled mostly by accident.
- Removed some long-deprecated markdown syntax for mentions. - Removed some long-deprecated Markdown syntax for mentions.
- Added support for clicking on a mention to see a user's profile. - Added support for clicking on a mention to see a user's profile.
- Links to logged-in content in Zulip now take the user to the - Links to logged-in content in Zulip now take the user to the
appropriate upload or view after a user logs in. appropriate upload or view after a user logs in.
@ -1117,7 +1117,7 @@ This is a security release, with a handful of cherry-picked changes
since 1.7.1. All Zulip server admins are encouraged to upgrade since 1.7.1. All Zulip server admins are encouraged to upgrade
promptly. promptly.
- CVE-2018-9986: Fix XSS issues with frontend markdown processor. - CVE-2018-9986: Fix XSS issues with frontend Markdown processor.
- CVE-2018-9987: Fix XSS issue with muting notifications. - CVE-2018-9987: Fix XSS issue with muting notifications.
- CVE-2018-9990: Fix XSS issue with stream names in topic typeahead. - CVE-2018-9990: Fix XSS issue with stream names in topic typeahead.
- CVE-2018-9999: Fix XSS issue with user uploads. The fix for this - CVE-2018-9999: Fix XSS issue with user uploads. The fix for this
@ -1310,7 +1310,7 @@ running a version from before 1.7 should upgrade directly to 1.7.1.
included in a single missed-message email. included in a single missed-message email.
- Fixed issues with inconsistent visual display of @-all mentions. - Fixed issues with inconsistent visual display of @-all mentions.
- Fixed zombie process leaks on servers with <4GB of RAM. - Fixed zombie process leaks on servers with <4GB of RAM.
- Fixed markdown previews of /me messages. - Fixed Markdown previews of /me messages.
- Fixed a subtle bug involving timestamps of locally echoed messages. - Fixed a subtle bug involving timestamps of locally echoed messages.
- Fixed the behavior of key combintions like Ctrl+Enter in the compose box. - Fixed the behavior of key combintions like Ctrl+Enter in the compose box.
- Worked around Google Compute Engine's default boto configuration, - Worked around Google Compute Engine's default boto configuration,
@ -1373,7 +1373,7 @@ Zulip apps.
* Added Basecamp, Gogs, Greenhouse, Home Assistant, Slack, Splunk, and * Added Basecamp, Gogs, Greenhouse, Home Assistant, Slack, Splunk, and
WordPress webhook integrations. WordPress webhook integrations.
* Added LaTeX support to the markdown processor. * Added LaTeX support to the Markdown processor.
* Added support for filtering branches to all Git integrations. * Added support for filtering branches to all Git integrations.
* Added read-only access to organization-level settings for all users. * Added read-only access to organization-level settings for all users.
* Added UI for managing muted topics and uploaded files. * Added UI for managing muted topics and uploaded files.
@ -1388,7 +1388,7 @@ Zulip apps.
* Added several new permissions-related organization settings. * Added several new permissions-related organization settings.
* Added new endpoint for fetching presence data, useful in employee directories. * Added new endpoint for fetching presence data, useful in employee directories.
* Added typeahead for language for syntax highlighting in code blocks. * Added typeahead for language for syntax highlighting in code blocks.
* Added support for basic markdown in stream descriptions. * Added support for basic Markdown in stream descriptions.
* Added email notifications on new Zulip logins. * Added email notifications on new Zulip logins.
* Added security hardening before serving uploaded files. * Added security hardening before serving uploaded files.
* Added new PRIVACY_POLICY setting to provide a Markdown privacy policy. * Added new PRIVACY_POLICY setting to provide a Markdown privacy policy.
@ -1412,7 +1412,7 @@ Zulip apps.
* Improved search typeahead to support group private messages. * Improved search typeahead to support group private messages.
* Improved logic for when the compose box should open/close. * Improved logic for when the compose box should open/close.
* Improved lightbox to support scrolling through images. * Improved lightbox to support scrolling through images.
* Improved markdown support for bulleted lists. * Improved Markdown support for bulleted lists.
* Improved copy-to-clipboard support in various places. * Improved copy-to-clipboard support in various places.
* Improved subject lines of missed message emails. * Improved subject lines of missed message emails.
* Improved handling of users trying to login with OAuth without an account. * Improved handling of users trying to login with OAuth without an account.
@ -1486,7 +1486,7 @@ Zulip apps.
#### Full feature changelog #### Full feature changelog
- Added an emoji picker/browser to the compose box. - Added an emoji picker/browser to the compose box.
- Added markdown preview support to the compose box. - Added Markdown preview support to the compose box.
- Added a new analytics system to track interesting usage statistics. - Added a new analytics system to track interesting usage statistics.
- Added a /stats page with graphs of the analytics data. - Added a /stats page with graphs of the analytics data.
- Added display of subscriber counts in Manage streams. - Added display of subscriber counts in Manage streams.
@ -1495,7 +1495,7 @@ Zulip apps.
- Added support for copying subscribers from existing streams on creation. - Added support for copying subscribers from existing streams on creation.
- Added several new search/filtering UI elements. - Added several new search/filtering UI elements.
- Added UI for deactivating your own Zulip account. - Added UI for deactivating your own Zulip account.
- Added support for viewing the raw markdown content of a message. - Added support for viewing the raw Markdown content of a message.
- Added support for deploying Zulip with subdomains for each realm. - Added support for deploying Zulip with subdomains for each realm.
This entailed numerous changes to ensure a consistent experience. This entailed numerous changes to ensure a consistent experience.
- Added support for (optionally) using PGRoonga to support full-text - Added support for (optionally) using PGRoonga to support full-text
@ -1522,12 +1522,12 @@ Zulip apps.
whether Zulip is optimized around protecting user privacy whether Zulip is optimized around protecting user privacy
vs. administrative control. vs. administrative control.
- Added #**streamName** syntax for linking to a stream. - Added #**streamName** syntax for linking to a stream.
- Added support for viewing markdown source of messages. - Added support for viewing Markdown source of messages.
- Added setting to always send push notifications. - Added setting to always send push notifications.
- Added setting to hide private message content in desktop - Added setting to hide private message content in desktop
notifications. notifications.
- Added buttons to download .zuliprc files. - Added buttons to download .zuliprc files.
- Added italics and strikethrough support in markdown implementation. - Added italics and strikethrough support in Markdown implementation.
- Added errors for common installations mistakes (e.g. too little RAM). - Added errors for common installations mistakes (e.g. too little RAM).
- Added a new /authors page showing the contributors to the current - Added a new /authors page showing the contributors to the current
Zulip version. Zulip version.
@ -1552,7 +1552,7 @@ Zulip apps.
- Fixed several transactionality bugs (e.g. in Huddle creation). - Fixed several transactionality bugs (e.g. in Huddle creation).
- Fixed missed-message email configuration error handling. - Fixed missed-message email configuration error handling.
- Fixed annoying @-mentions in Jira integration. - Fixed annoying @-mentions in Jira integration.
- Fixed various mismatches between frontend and backend markdown - Fixed various mismatches between frontend and backend Markdown
implementations. implementations.
- Fixed various popover-related UI bugs. - Fixed various popover-related UI bugs.
- Fixed duplicate notifications with multiple open Zulip tabs. - Fixed duplicate notifications with multiple open Zulip tabs.
@ -1621,7 +1621,7 @@ Zulip apps.
- Added year to timestamps in message interstitials for old messages. - Added year to timestamps in message interstitials for old messages.
- Added GitHub authentication (and integrated python-social-auth, so it's - Added GitHub authentication (and integrated python-social-auth, so it's
easy to add additional social authentication methods). easy to add additional social authentication methods).
- Added TERMS_OF_SERVICE setting using markdown formatting to configure - Added TERMS_OF_SERVICE setting using Markdown formatting to configure
the terms of service for a Zulip server. the terms of service for a Zulip server.
- Added numerous hooks to puppet modules to enable more configurations. - Added numerous hooks to puppet modules to enable more configurations.
- Moved several useful puppet components into the main puppet - Moved several useful puppet components into the main puppet
@ -1670,7 +1670,7 @@ Zulip apps.
- Fixed old deployment directories leaking indefinitely. - Fixed old deployment directories leaking indefinitely.
- Fixed need to manually add localhost in ALLOWED_HOSTS. - Fixed need to manually add localhost in ALLOWED_HOSTS.
- Fixed display positioning for the color picker on subscriptions page. - Fixed display positioning for the color picker on subscriptions page.
- Fixed escaping of Zulip extensions to markdown. - Fixed escaping of Zulip extensions to Markdown.
- Fixed requiring a reload to see newly uploaded avatars. - Fixed requiring a reload to see newly uploaded avatars.
- Fixed @all warning firing even for `@all`. - Fixed @all warning firing even for `@all`.
- Restyled password reset form to look nice. - Restyled password reset form to look nice.

View File

@ -132,7 +132,7 @@ help](https://chat.zulip.org/#narrow/stream/31-production-help) in the
[Zulip development community [Zulip development community
server](../contributing/chat-zulip-org.md) for best-effort help. server](../contributing/chat-zulip-org.md) for best-effort help.
Please include the relevant error output from the above logs in a Please include the relevant error output from the above logs in a
[markdown code [Markdown code
block](https://zulip.com/help/format-your-message-using-markdown#code) block](https://zulip.com/help/format-your-message-using-markdown#code)
in any reports. in any reports.

View File

@ -269,7 +269,7 @@ Zulip uses the [iamcal emoji data package][iamcal] for its emoji data
and sprite sheets. We download this dependency using `npm`, and then and sprite sheets. We download this dependency using `npm`, and then
have a tool, `tools/setup/build_emoji`, which reformats the emoji data have a tool, `tools/setup/build_emoji`, which reformats the emoji data
into the files under `static/generated/emoji`. Those files are in into the files under `static/generated/emoji`. Those files are in
turn used by our [markdown processor](../subsystems/markdown.md) and turn used by our [Markdown processor](../subsystems/markdown.md) and
`tools/update-prod-static` to make Zulip's emoji work in the various `tools/update-prod-static` to make Zulip's emoji work in the various
environments where they need to be displayed. environments where they need to be displayed.
@ -295,10 +295,10 @@ implementation of that tool.
### Pygments data ### Pygments data
The list of languages supported by our markdown syntax highlighting The list of languages supported by our Markdown syntax highlighting
comes from the [pygments][] package. `tools/setup/build_pygments_data` is comes from the [pygments][] package. `tools/setup/build_pygments_data` is
responsible for generating `static/generated/pygments_data.json` so that responsible for generating `static/generated/pygments_data.json` so that
our JavaScript markdown processor has access to the supported list. our JavaScript Markdown processor has access to the supported list.
## Modifying provisioning ## Modifying provisioning

View File

@ -79,7 +79,7 @@ The emoji tree generated by this process contains several import elements:
* `images/emoji/*.png`: A farm of symlinks from emoji names to the * `images/emoji/*.png`: A farm of symlinks from emoji names to the
`images/emoji/unicode/` tree. This is used to serve individual emoji `images/emoji/unicode/` tree. This is used to serve individual emoji
images, as well as for the images, as well as for the
[backend markdown processor](../subsystems/markdown.md) to know which emoji [backend Markdown processor](../subsystems/markdown.md) to know which emoji
names exist and what unicode emoji / images they map to. In this names exist and what unicode emoji / images they map to. In this
tree, we currently include all of the emoji in `emoji-map.json`; tree, we currently include all of the emoji in `emoji-map.json`;
this means that if you send `:angry_face:`, it won't autocomplete, this means that if you send `:angry_face:`, it won't autocomplete,

View File

@ -87,7 +87,7 @@ The format of this output is:
* HTTP status code * HTTP status code
* Time to process * Time to process
* (Optional perf data details, e.g. database time/queries, memcached * (Optional perf data details, e.g. database time/queries, memcached
time/queries, Django process startup time, markdown processing time, time/queries, Django process startup time, Markdown processing time,
etc.) etc.)
* Endpoint/URL from zproject/urls.py * Endpoint/URL from zproject/urls.py
* "email via client" showing user account involved (if logged in) and * "email via client" showing user account involved (if logged in) and
@ -95,7 +95,7 @@ the type of client they used ("web", "Android", etc.).
The performance data details are particularly useful for investigating The performance data details are particularly useful for investigating
performance problems, since one can see at a glance whether a slow performance problems, since one can see at a glance whether a slow
request was caused by delays in the database, in the markdown request was caused by delays in the database, in the Markdown
processor, in memcached, or in other Python code. processor, in memcached, or in other Python code.
One useful thing to note, however, is that the database time is only One useful thing to note, however, is that the database time is only

View File

@ -23,7 +23,7 @@ trip from the server. Those frontend renderings are only shown to the
sender of a message, and they are (ideally) identical to the backend sender of a message, and they are (ideally) identical to the backend
rendering. rendering.
The JavaScript markdown implementation has a function, The JavaScript Markdown implementation has a function,
`markdown.contains_backend_only_syntax`, that is used to check whether a message `markdown.contains_backend_only_syntax`, that is used to check whether a message
contains any syntax that needs to be rendered to HTML on the backend. contains any syntax that needs to be rendered to HTML on the backend.
If `markdown.contains_backend_only_syntax` returns true, the frontend simply won't If `markdown.contains_backend_only_syntax` returns true, the frontend simply won't
@ -46,10 +46,10 @@ The Python-Markdown implementation is tested by
A shared set of fixed test data ("test fixtures") is present in A shared set of fixed test data ("test fixtures") is present in
`zerver/tests/fixtures/markdown_test_cases.json`, and is automatically used `zerver/tests/fixtures/markdown_test_cases.json`, and is automatically used
by both test suites; as a result, it is the preferred place to add new by both test suites; as a result, it is the preferred place to add new
tests for Zulip's markdown system. Some important notes on reading tests for Zulip's Markdown system. Some important notes on reading
this file: this file:
* `expected_output` is the expected output for the backend markdown * `expected_output` is the expected output for the backend Markdown
processor. processor.
* When the frontend processor doesn't support a feature and it should * When the frontend processor doesn't support a feature and it should
just be rendered on the backend, we set `backend_only_rendering` to just be rendered on the backend, we set `backend_only_rendering` to
@ -87,45 +87,45 @@ is a workaround due to lack of comments support in JSON. Revert your
tests with `tools/test-js-with-node markdown` and backend tests with tests with `tools/test-js-with-node markdown` and backend tests with
`tools/test-backend zerver.tests.test_markdown.MarkdownTest.test_markdown_fixtures`. `tools/test-backend zerver.tests.test_markdown.MarkdownTest.test_markdown_fixtures`.
## Changing Zulip's markdown processor ## Changing Zulip's Markdown processor
First, you will likely find these third-party resources helpful: First, you will likely find these third-party resources helpful:
* **[Python-Markdown](https://pypi.python.org/pypi/Markdown)** is the markdown * **[Python-Markdown](https://pypi.python.org/pypi/Markdown)** is the Markdown
library used by Zulip as a base to build our custom markdown syntax upon. library used by Zulip as a base to build our custom Markdown syntax upon.
* **[Python's XML ElementTree](https://docs.python.org/3/library/xml.etree.elementtree.html)** * **[Python's XML ElementTree](https://docs.python.org/3/library/xml.etree.elementtree.html)**
is the part of the Python standard library used by Python Markdown is the part of the Python standard library used by Python Markdown
and any custom extensions to generate and modify the output HTML. and any custom extensions to generate and modify the output HTML.
When changing Zulip's markdown syntax, you need to update several When changing Zulip's Markdown syntax, you need to update several
places: places:
* The backend markdown processor (`zerver/lib/markdown/__init__.py`). * The backend Markdown processor (`zerver/lib/markdown/__init__.py`).
* The frontend markdown processor (`static/js/markdown.js` and sometimes * The frontend Markdown processor (`static/js/markdown.js` and sometimes
`static/third/marked/lib/marked.js`), or `markdown.contains_backend_only_syntax` if `static/third/marked/lib/marked.js`), or `markdown.contains_backend_only_syntax` if
your changes won't be supported in the frontend processor. your changes won't be supported in the frontend processor.
* If desired, the typeahead logic in `static/js/composebox_typeahead.js`. * If desired, the typeahead logic in `static/js/composebox_typeahead.js`.
* The test suite, probably via adding entries to `zerver/tests/fixtures/markdown_test_cases.json`. * The test suite, probably via adding entries to `zerver/tests/fixtures/markdown_test_cases.json`.
* The in-app markdown documentation (`templates/zerver/app/markdown_help.html`). * The in-app Markdown documentation (`templates/zerver/app/markdown_help.html`).
* The list of changes to markdown at the end of this document. * The list of changes to Markdown at the end of this document.
Important considerations for any changes are: Important considerations for any changes are:
* Security: A bug in the markdown processor can lead to XSS issues. * Security: A bug in the Markdown processor can lead to XSS issues.
For example, we should not insert unsanitized HTML from a For example, we should not insert unsanitized HTML from a
third-party web application into a Zulip message. third-party web application into a Zulip message.
* Uniqueness: We want to avoid users having a bad experience due to * Uniqueness: We want to avoid users having a bad experience due to
accidentally triggering markdown syntax or typeahead that isn't accidentally triggering Markdown syntax or typeahead that isn't
related to what they are trying to express. related to what they are trying to express.
* Performance: Zulip can render a lot of messages very quickly, and * Performance: Zulip can render a lot of messages very quickly, and
we'd like to keep it that way. New regular expressions similar to we'd like to keep it that way. New regular expressions similar to
the ones already present are unlikely to be a problem, but we need the ones already present are unlikely to be a problem, but we need
to be thoughtful about expensive computations or third-party API to be thoughtful about expensive computations or third-party API
requests. requests.
* Database: The backend markdown processor runs inside a Python thread * Database: The backend Markdown processor runs inside a Python thread
(as part of how we implement timeouts for third-party API queries), (as part of how we implement timeouts for third-party API queries),
and for that reason we currently should avoid making database and for that reason we currently should avoid making database
queries inside the markdown processor. This is a technical queries inside the Markdown processor. This is a technical
implementation detail that could be changed with a few days of work, implementation detail that could be changed with a few days of work,
but is an important detail to know about until we do that work. but is an important detail to know about until we do that work.
* Testing: Every new feature should have both positive and negative * Testing: Every new feature should have both positive and negative
@ -134,7 +134,7 @@ Important considerations for any changes are:
## Per-realm features ## Per-realm features
Zulip's markdown processor's rendering supports a number of features Zulip's Markdown processor's rendering supports a number of features
that depend on realm-specific or user-specific data. For example, the that depend on realm-specific or user-specific data. For example, the
realm could have realm could have
[Linkifiers](https://zulip.com/help/add-a-custom-linkification-filter) [Linkifiers](https://zulip.com/help/add-a-custom-linkification-filter)
@ -146,9 +146,9 @@ At a backend code level, these are controlled by the `message_realm`
object and other arguments passed into `do_convert` (`sent_by_bot`, object and other arguments passed into `do_convert` (`sent_by_bot`,
`translate_emoticons`, `mention_data`, etc.). Because `translate_emoticons`, `mention_data`, etc.). Because
`python-markdown` doesn't support directly passing arguments into the `python-markdown` doesn't support directly passing arguments into the
markdown processor, our logic attaches these data to the Markdown Markdown processor, our logic attaches these data to the Markdown
processor object via e.g. `_md_engine.zulip_db_data`, and then processor object via e.g. `_md_engine.zulip_db_data`, and then
individual markdown rules can access the data from there. individual Markdown rules can access the data from there.
For non-message contexts (e.g. an organization's profile (aka the For non-message contexts (e.g. an organization's profile (aka the
thing on the right-hand side of the login page), stream descriptions, thing on the right-hand side of the login page), stream descriptions,

View File

@ -65,13 +65,13 @@ number of purposes:
* Handling the [local echo details](#local-echo). * Handling the [local echo details](#local-echo).
* Handling certain client configuration options that affect * Handling certain client configuration options that affect
messages. E.g. determining whether to send the messages. E.g. determining whether to send the
plaintext/markdown raw content or the rendered HTML (e.g. the plaintext/Markdown raw content or the rendered HTML (e.g. the
`apply_markdown` and `client_gravatar` features in our `apply_markdown` and `client_gravatar` features in our
[events API docs](https://zulip.com/api/register-queue)). [events API docs](https://zulip.com/api/register-queue)).
* Following our standard naming convention, input validation is done * Following our standard naming convention, input validation is done
inside the `check_message` function, which is responsible for inside the `check_message` function, which is responsible for
validating the user can send to the recipient, validating the user can send to the recipient,
[rendering the markdown](../subsystems/markdown.md), etc. -- [rendering the Markdown](../subsystems/markdown.md), etc. --
basically everything that can fail due to bad user input. basically everything that can fail due to bad user input.
* The core `do_send_messages` function (which handles actually sending * The core `do_send_messages` function (which handles actually sending
the message) is one of the most optimized and thus complex parts of the message) is one of the most optimized and thus complex parts of
@ -113,10 +113,10 @@ browser, and then replace it with data from the server when it
changes. changes.
Zulip aims for a near-perfect local echo experience, which requires is Zulip aims for a near-perfect local echo experience, which requires is
why our [markdown system](../subsystems/markdown.md) requires both why our [Markdown system](../subsystems/markdown.md) requires both
an authoritative (backend) markdown implementation and a secondary an authoritative (backend) Markdown implementation and a secondary
(frontend) markdown implementation, the latter used only for the local (frontend) Markdown implementation, the latter used only for the local
echo feature. Read our markdown documentation for all the tricky echo feature. Read our Markdown documentation for all the tricky
details on how that works and is tested. details on how that works and is tested.
The rest of this section details how Zulip manages locally echoed The rest of this section details how Zulip manages locally echoed
@ -231,13 +231,13 @@ from the target URL, and for slow websites, this could result in a
significant delay in rendering the message and delivering it to other significant delay in rendering the message and delivering it to other
users. users.
* For this case, Zulip's backend markdown processor will render the * For this case, Zulip's backend Markdown processor will render the
message without including the URL embeds/previews, but it will add a message without including the URL embeds/previews, but it will add a
deferred work item into the `embed_links` queue. deferred work item into the `embed_links` queue.
* The [queue processor](../subsystems/queuing.md) for the * The [queue processor](../subsystems/queuing.md) for the
`embed_links` queue will fetch the URLs, and then if they return `embed_links` queue will fetch the URLs, and then if they return
results, rerun the markdown processor and notify clients of the results, rerun the Markdown processor and notify clients of the
updated message `rendered_content`. updated message `rendered_content`.
* We reuse the `update_message` framework (used for * We reuse the `update_message` framework (used for

View File

@ -197,7 +197,7 @@ exercise is about 30 minutes, assuming no bugs.
### Composing messages ### ### Composing messages ###
We have pretty good automated tests for our markdown processor, so We have pretty good automated tests for our Markdown processor, so
manual testing is targeted more to other interactions. For composing manual testing is targeted more to other interactions. For composing
a message, pay attention to details like what is automatically a message, pay attention to details like what is automatically
populated and where the focus is placed. populated and where the focus is placed.
@ -224,7 +224,7 @@ populated and where the focus is placed.
existing topic. existing topic.
- Formatting stuff - Formatting stuff
- Use the "A" icon to get markdown help. - Use the "A" icon to get Markdown help.
- Use the eyeball icon to show a preview and send from preview mode. - Use the eyeball icon to show a preview and send from preview mode.
- Toggle in and out of preview before sending a message. - Toggle in and out of preview before sending a message.
- Use @-mention to mention Hamlet (and send him a message). - Use @-mention to mention Hamlet (and send him a message).

View File

@ -222,7 +222,7 @@ casper.then(function () {
casper.fill( casper.fill(
'form[action^="/json/messages"]', 'form[action^="/json/messages"]',
{ {
content: "**Markdown Preview** >> Test for markdown preview", content: "**Markdown Preview** >> Test for Markdown preview",
}, },
false false
); );
@ -235,8 +235,8 @@ casper.then(function () {
casper.waitForSelectorTextChange("#preview_content", function () { casper.waitForSelectorTextChange("#preview_content", function () {
casper.test.assertEquals( casper.test.assertEquals(
casper.getHTML("#preview_content"), casper.getHTML("#preview_content"),
"<p><strong>Markdown Preview</strong> &gt;&gt; Test for markdown preview</p>", "<p><strong>Markdown Preview</strong> &gt;&gt; Test for Markdown preview</p>",
"Check markdown is previewed properly" "Check Markdown is previewed properly"
); );
}); });
}); });

View File

@ -535,7 +535,7 @@ run_test("markdown_shortcuts", () => {
compose.handle_keydown(event, $("#compose-textarea")); compose.handle_keydown(event, $("#compose-textarea"));
} }
// This function cross tests the cmd/ctrl + markdown shortcuts in // This function cross tests the cmd/ctrl + Markdown shortcuts in
// Mac and Linux/Windows environments. So in short, this tests // Mac and Linux/Windows environments. So in short, this tests
// that e.g. Cmd+B should be ignored on Linux/Windows and Ctrl+B // that e.g. Cmd+B should be ignored on Linux/Windows and Ctrl+B
// should be ignored on Mac. // should be ignored on Mac.
@ -569,9 +569,9 @@ run_test("markdown_shortcuts", () => {
// Default (Linux/Windows) userAgent tests: // Default (Linux/Windows) userAgent tests:
test_i_typed(false, false); test_i_typed(false, false);
// Check all the ctrl + markdown shortcuts work correctly // Check all the ctrl + Markdown shortcuts work correctly
all_markdown_test(true, false); all_markdown_test(true, false);
// The Cmd + markdown shortcuts should do nothing on Linux/Windows // The Cmd + Markdown shortcuts should do nothing on Linux/Windows
os_specific_markdown_test(false, true); os_specific_markdown_test(false, true);
// Setting following platform to test in mac env // Setting following platform to test in mac env
@ -579,9 +579,9 @@ run_test("markdown_shortcuts", () => {
// Mac userAgent tests: // Mac userAgent tests:
test_i_typed(false, false); test_i_typed(false, false);
// The ctrl + markdown shortcuts should do nothing on mac // The ctrl + Markdown shortcuts should do nothing on mac
os_specific_markdown_test(true, false); os_specific_markdown_test(true, false);
// Check all the Cmd + markdown shortcuts work correctly // Check all the Cmd + Markdown shortcuts work correctly
all_markdown_test(false, true); all_markdown_test(false, true);
// Reset userAgent // Reset userAgent

View File

@ -178,7 +178,7 @@ stream_data.add_sub(edgecase_stream_2);
stream_data.add_sub(amp_stream); stream_data.add_sub(amp_stream);
// Check the default behavior of fenced code blocks // Check the default behavior of fenced code blocks
// works properly before markdown is initialized. // works properly before Markdown is initialized.
run_test("fenced_block_defaults", () => { run_test("fenced_block_defaults", () => {
const input = "\n```\nfenced code\n```\n\nand then after\n"; const input = "\n```\nfenced code\n```\n\nand then after\n";
const expected = const expected =

View File

@ -192,12 +192,12 @@ async function test_markdown_rendering(page) {
"", "",
); );
await common.fill_form(page, 'form[action^="/json/messages"]', { await common.fill_form(page, 'form[action^="/json/messages"]', {
content: "**Markdown Preview** >> Test for markdown preview", content: "**Markdown Preview** >> Test for Markdown preview",
}); });
await page.click("#markdown_preview"); await page.click("#markdown_preview");
await page.waitForSelector("#preview_content", {visible: true}); await page.waitForSelector("#preview_content", {visible: true});
const expected_markdown_html = const expected_markdown_html =
"<p><strong>Markdown Preview</strong> &gt;&gt; Test for markdown preview</p>"; "<p><strong>Markdown Preview</strong> &gt;&gt; Test for Markdown preview</p>";
await page.waitForFunction(() => $("#preview_content").html() !== ""); await page.waitForFunction(() => $("#preview_content").html() !== "");
markdown_preview_element = await page.$("#preview_content"); markdown_preview_element = await page.$("#preview_content");
assert.equal( assert.equal(

View File

@ -93,7 +93,7 @@ function short_tb(tb) {
return lines.splice(0, i + 1).join("\n") + "\n(...)\n"; return lines.splice(0, i + 1).join("\n") + "\n(...)\n";
} }
// Set up markdown comparison helper // Set up Markdown comparison helper
global.markdown_assert = require("./markdown_assert.js"); global.markdown_assert = require("./markdown_assert.js");
let current_file_name; let current_file_name;

View File

@ -14,7 +14,7 @@ dataclasses;python_version<"3.7"
# Needed for rendering backend templates # Needed for rendering backend templates
Jinja2 Jinja2
# Needed for markdown processing # Needed for Markdown processing
Markdown Markdown
importlib-metadata;python_version<"3.8" # for Markdown importlib-metadata;python_version<"3.8" # for Markdown
Pygments Pygments
@ -69,7 +69,7 @@ premailer
# Needed for JWT-based auth # Needed for JWT-based auth
PyJWT PyJWT
# Needed for including other markdown files for user docs # Needed for including other Markdown files for user docs
markdown-include markdown-include
# Needed to access rabbitmq # Needed to access rabbitmq

View File

@ -11,5 +11,5 @@
sphinx sphinx
sphinx-rtd-theme sphinx-rtd-theme
# Needed to build markdown docs # Needed to build Markdown docs
recommonmark recommonmark

View File

@ -37,7 +37,7 @@ exports.uploads_re = new RegExp(
); );
function make_uploads_relative(content) { function make_uploads_relative(content) {
// Rewrite uploads in markdown links back to domain-relative form // Rewrite uploads in Markdown links back to domain-relative form
return content.replace(exports.uploads_re, "]($1)"); return content.replace(exports.uploads_re, "]($1)");
} }
@ -857,8 +857,8 @@ exports.render_and_show_preview = function (preview_spinner, preview_content_box
loading.make_indicator(spinner); loading.make_indicator(spinner);
} else { } else {
// For messages that don't appear to contain syntax that // For messages that don't appear to contain syntax that
// is only supported by our backend markdown processor, we // is only supported by our backend Markdown processor, we
// render using the frontend markdown processor (but still // render using the frontend Markdown processor (but still
// render server-side to ensure the preview is accurate; // render server-side to ensure the preview is accurate;
// if the `markdown.contains_backend_only_syntax` logic is // if the `markdown.contains_backend_only_syntax` logic is
// wrong, users will see a brief flicker of the locally // wrong, users will see a brief flicker of the locally

View File

@ -272,7 +272,7 @@ exports.format_draft = function (draft) {
markdown.apply_markdown(formatted); markdown.apply_markdown(formatted);
} catch (error) { } catch (error) {
// In the unlikely event that there is syntax in the // In the unlikely event that there is syntax in the
// draft content which our markdown processor is // draft content which our Markdown processor is
// unable to process, we delete the draft, so that the // unable to process, we delete the draft, so that the
// drafts overlay can be opened without any errors. // drafts overlay can be opened without any errors.
// We also report the exception to the server so that // We also report the exception to the server so that

View File

@ -232,7 +232,7 @@ exports.edit_locally = function (message, request) {
message.mentioned_me_directly = request.mentioned_me_directly; message.mentioned_me_directly = request.mentioned_me_directly;
message.alerted = request.alerted; message.alerted = request.alerted;
} else { } else {
// Otherwise, we markdown-render the message; this resets // Otherwise, we Markdown-render the message; this resets
// all flags, so we need to restore those flags that are // all flags, so we need to restore those flags that are
// properties of how the user has interacted with the // properties of how the user has interacted with the
// message, and not its rendering. // message, and not its rendering.

View File

@ -675,7 +675,7 @@ class Filter {
if (this.has_operator("has") && is_local_echo) { if (this.has_operator("has") && is_local_echo) {
// The has: operators can be applied locally for messages // The has: operators can be applied locally for messages
// rendered by the backend; links, attachments, and images // rendered by the backend; links, attachments, and images
// are not handled properly by the local echo markdown // are not handled properly by the local echo Markdown
// processor. // processor.
return false; return false;
} }

View File

@ -8,9 +8,9 @@ const emoji = require("../shared/js/emoji");
const fenced_code = require("../shared/js/fenced_code"); const fenced_code = require("../shared/js/fenced_code");
const marked = require("../third/marked/lib/marked"); const marked = require("../third/marked/lib/marked");
// This contains zulip's frontend markdown implementation; see // This contains zulip's frontend Markdown implementation; see
// docs/subsystems/markdown.md for docs on our Markdown syntax. The other // docs/subsystems/markdown.md for docs on our Markdown syntax. The other
// main piece in rendering markdown client-side is // main piece in rendering Markdown client-side is
// static/third/marked/lib/marked.js, which we have significantly // static/third/marked/lib/marked.js, which we have significantly
// modified from the original implementation. // modified from the original implementation.
@ -25,7 +25,7 @@ let helpers;
const realm_filter_map = new Map(); const realm_filter_map = new Map();
let realm_filter_list = []; let realm_filter_list = [];
// Regexes that match some of our common backend-only markdown syntax // Regexes that match some of our common backend-only Markdown syntax
const backend_only_markdown_re = [ const backend_only_markdown_re = [
// Inline image previews, check for contiguous chars ending in image suffix // Inline image previews, check for contiguous chars ending in image suffix
// To keep the below regexes simple, split them out for the end-of-message case // To keep the below regexes simple, split them out for the end-of-message case
@ -80,7 +80,7 @@ exports.translate_emoticons_to_names = (text) => {
exports.contains_backend_only_syntax = function (content) { exports.contains_backend_only_syntax = function (content) {
// Try to guess whether or not a message contains syntax that only the // Try to guess whether or not a message contains syntax that only the
// backend markdown processor can correctly handle. // backend Markdown processor can correctly handle.
// If it doesn't, we can immediately render it client-side for local echo. // If it doesn't, we can immediately render it client-side for local echo.
const markedup = backend_only_markdown_re.find((re) => re.test(content)); const markedup = backend_only_markdown_re.find((re) => re.test(content));
@ -319,7 +319,7 @@ function handleTimestamp(time) {
if (isNaN(time)) { if (isNaN(time)) {
// Moment throws a large deprecation warning when it has to fallback // Moment throws a large deprecation warning when it has to fallback
// to the Date() constructor. We needn't worry here and can let backend // to the Date() constructor. We needn't worry here and can let backend
// markdown handle any dates that moment misses. // Markdown handle any dates that moment misses.
moment.suppressDeprecationWarnings = true; moment.suppressDeprecationWarnings = true;
timeobject = moment(time); // not a Unix timestamp timeobject = moment(time); // not a Unix timestamp
} else { } else {
@ -501,7 +501,7 @@ exports.initialize = function (realm_filters, helper_config) {
}; };
} }
// Configure the marked markdown parser for our usage // Configure the marked Markdown parser for our usage
const r = new marked.Renderer(); const r = new marked.Renderer();
// No <code> around our code blocks instead a codehilite <div> and disable // No <code> around our code blocks instead a codehilite <div> and disable
@ -512,7 +512,7 @@ exports.initialize = function (realm_filters, helper_config) {
const old_link = r.link; const old_link = r.link;
r.link = (href, title, text) => old_link.call(r, href, title, text.trim() ? text : href); r.link = (href, title, text) => old_link.call(r, href, title, text.trim() ? text : href);
// Put a newline after a <br> in the generated HTML to match markdown // Put a newline after a <br> in the generated HTML to match Markdown
r.br = function () { r.br = function () {
return "<br>\n"; return "<br>\n";
}; };

View File

@ -9,11 +9,11 @@
I also wanted to make some diffs clear before I also wanted to make some diffs clear before
doing any major file moves. doing any major file moves.
Also, I want the unit tests for markdown to Also, I want the unit tests for Markdown to
be able to reuse this code easily (and therefore be able to reuse this code easily (and therefore
didn't just put this in ui_init.js). didn't just put this in ui_init.js).
Once the first steps of making markdown be a Once the first steps of making Markdown be a
shared library are complete, we may tweak shared library are complete, we may tweak
the file organization a bit. the file organization a bit.

View File

@ -521,7 +521,7 @@ class MessageListView {
// (and is not possible to handle solely via CSS), this is // (and is not possible to handle solely via CSS), this is
// where we modify the content. It is a goal to minimize how // where we modify the content. It is a goal to minimize how
// much logic is present in this function; wherever possible, // much logic is present in this function; wherever possible,
// we should implement features with the markdown processor, // we should implement features with the Markdown processor,
// HTML and CSS. // HTML and CSS.
if (row.length !== 1) { if (row.length !== 1) {

View File

@ -739,7 +739,7 @@ exports.register_click_handlers = function () {
$("#main_div").on("click", ".user-mention", function (e) { $("#main_div").on("click", ".user-mention", function (e) {
const id_string = $(this).attr("data-user-id"); const id_string = $(this).attr("data-user-id");
// We fallback to email to handle legacy markdown that was rendered // We fallback to email to handle legacy Markdown that was rendered
// before we cut over to using data-user-id // before we cut over to using data-user-id
const email = $(this).attr("data-user-email"); const email = $(this).attr("data-user-email");
if (id_string === "*" || email === "*") { if (id_string === "*" || email === "*") {

View File

@ -9,13 +9,13 @@ const moment = require("moment");
update any renamed users/streams/groups etc. and other update any renamed users/streams/groups etc. and other
dynamic parts of our rendered messages. dynamic parts of our rendered messages.
Use this module wherever some markdown rendered content Use this module wherever some Markdown rendered content
is being displayed. is being displayed.
*/ */
function get_user_id_for_mention_button(elem) { function get_user_id_for_mention_button(elem) {
const user_id_string = $(elem).attr("data-user-id"); const user_id_string = $(elem).attr("data-user-id");
// Handle legacy markdown that was rendered before we cut // Handle legacy Markdown that was rendered before we cut
// over to using data-user-id. // over to using data-user-id.
const email = $(elem).attr("data-user-email"); const email = $(elem).attr("data-user-email");

View File

@ -55,7 +55,7 @@ exports.initialize = function () {
const spoiler_content = $(this).siblings(".spoiler-content"); const spoiler_content = $(this).siblings(".spoiler-content");
const target = $(e.target); const target = $(e.target);
// Spoiler headers can contain markdown, including links. We // Spoiler headers can contain Markdown, including links. We
// return so that clicking such links will be be processed by // return so that clicking such links will be be processed by
// the browser rather than opening the header. // the browser rather than opening the header.
if (target.closest("a").length > 0) { if (target.closest("a").length > 0) {

View File

@ -170,7 +170,7 @@ exports.render_date = function (time, time_above, today) {
return node; return node;
}; };
// Renders the timestamp returned by the <time:> markdown syntax. // Renders the timestamp returned by the <time:> Markdown syntax.
exports.render_markdown_timestamp = function (time, text) { exports.render_markdown_timestamp = function (time, text) {
const hourformat = page_params.twenty_four_hour_time ? "HH:mm" : "h:mm A"; const hourformat = page_params.twenty_four_hour_time ? "HH:mm" : "h:mm A";
const timestring = time.format("ddd, MMM D YYYY, " + hourformat); const timestring = time.format("ddd, MMM D YYYY, " + hourformat);

View File

@ -288,7 +288,7 @@ exports.clean_user_content_links = function (html) {
// rel="opener noreferrer". This ensures that external links // rel="opener noreferrer". This ensures that external links
// never replace the Zulip webapp while also protecting // never replace the Zulip webapp while also protecting
// against reverse tabnapping attacks, without relying on the // against reverse tabnapping attacks, without relying on the
// correctness of how Zulip's markdown processor generates links. // correctness of how Zulip's Markdown processor generates links.
// //
// Fragment links, which we intend to only open within the // Fragment links, which we intend to only open within the
// Zulip webapp using our hashchange system, do not require // Zulip webapp using our hashchange system, do not require

View File

@ -98,7 +98,7 @@ export function get_realm_emoji_url(emoji_name) {
// may have hand-typed an invalid emoji. // may have hand-typed an invalid emoji.
// The caller can check the result for falsiness // The caller can check the result for falsiness
// and then try alternate ways of parsing the // and then try alternate ways of parsing the
// emoji (in the case of markdown) or just do // emoji (in the case of Markdown) or just do
// whatever makes sense for the caller. // whatever makes sense for the caller.
return; return;
} }

View File

@ -78,7 +78,7 @@
border-right: 5px solid hsl(0, 0%, 87%); border-right: 5px solid hsl(0, 0%, 87%);
} }
/* Formatting for markdown tables */ /* Formatting for Markdown tables */
table { table {
padding-right: 10px; padding-right: 10px;
margin: 5px 5px 5px 5px; margin: 5px 5px 5px 5px;

View File

@ -41,7 +41,7 @@ No changes; feature level used for Zulip 3.0 release.
**Feature level 24** **Feature level 24**
* The `!avatar()` and `!gravatar()` markdown syntax, which was never * The `!avatar()` and `!gravatar()` Markdown syntax, which was never
documented, had inconsistent syntax, and was rarely used, was documented, had inconsistent syntax, and was rarely used, was
removed. removed.
@ -106,7 +106,7 @@ No changes; feature level used for Zulip 3.0 release.
**Feature level 15** **Feature level 15**
* Added [spoilers](/help/format-your-message-using-markdown#spoilers) * Added [spoilers](/help/format-your-message-using-markdown#spoilers)
to supported markdown features. to supported Markdown features.
**Feature level 14** **Feature level 14**
@ -166,7 +166,7 @@ No changes; feature level used for Zulip 3.0 release.
and [`GET /users/me`](/api/get-own-user): User objects now contain the and [`GET /users/me`](/api/get-own-user): User objects now contain the
`is_owner` field as well. `is_owner` field as well.
* Added [time mentions](/help/format-your-message-using-markdown#mention-a-time) * Added [time mentions](/help/format-your-message-using-markdown#mention-a-time)
to supported markdown features. to supported Markdown features.
**Feature level 7** **Feature level 7**

View File

@ -1,4 +1,4 @@
# Get a message's raw markdown # Get a message's raw Markdown
{generate_api_description(/messages/{message_id}:get)} {generate_api_description(/messages/{message_id}:get)}

View File

@ -108,7 +108,7 @@ below are for a webhook named `MyWebHook`.
## General advice ## General advice
* Consider using our Zulip markup to make the output from your * Consider using our Zulip markup to make the output from your
integration especially attractive or useful (e.g. emoji, markdown integration especially attractive or useful (e.g. emoji, Markdown
emphasis or @-mentions). emphasis or @-mentions).
* Use topics effectively to ensure sequential messages about the same * Use topics effectively to ensure sequential messages about the same

View File

@ -95,7 +95,7 @@ A correctly implemented endpoint will do the following:
* It will calculate a response that we call the "content" of * It will calculate a response that we call the "content" of
the response. the response.
* It will encode the content in Zulip's flavor of markdown (or * It will encode the content in Zulip's flavor of Markdown (or
just plain text). just plain text).
* It will then make a dictionary with key of "content" and * It will then make a dictionary with key of "content" and
the value being that content. (Note that "response_string" is the value being that content. (Note that "response_string" is

View File

@ -2,7 +2,7 @@
{% set entrypoint = "landing-page" %} {% set entrypoint = "landing-page" %}
{% set OPEN_GRAPH_TITLE = 'Modern chat for open source' %} {% set OPEN_GRAPH_TITLE = 'Modern chat for open source' %}
{% set OPEN_GRAPH_DESCRIPTION = 'No message limits, rich moderation features, markdown support, and a conversation model that scales to thousands of users.' %} {% set OPEN_GRAPH_DESCRIPTION = 'No message limits, rich moderation features, Markdown support, and a conversation model that scales to thousands of users.' %}
{% block title %} {% block title %}
<title>Zulip: the best group chat for open source projects</title> <title>Zulip: the best group chat for open source projects</title>

View File

@ -109,8 +109,8 @@ to mark the appropriate users as administrators.
organization using [this organization using [this
tool](https://github.com/minrk/archive-gitter/pull/5). tool](https://github.com/minrk/archive-gitter/pull/5).
- This tool doesn't translate Gitter's markdown format into Zulip - This tool doesn't translate Gitter's Markdown format into Zulip
format markdown (there are a few corner cases where the syntax is format Markdown (there are a few corner cases where the syntax is
different). Additionally, Gitter's different). Additionally, Gitter's
[issue mentions](https://gitter.zendesk.com/hc/en-us/articles/200176692-Issue-and-Pull-Request-mentions) [issue mentions](https://gitter.zendesk.com/hc/en-us/articles/200176692-Issue-and-Pull-Request-mentions)
aren't translated into anything yet. aren't translated into anything yet.

View File

@ -9,7 +9,7 @@
* [Add an emoji reaction](/api/add-reaction) * [Add an emoji reaction](/api/add-reaction)
* [Remove an emoji reaction](/api/remove-reaction) * [Remove an emoji reaction](/api/remove-reaction)
* [Render a message](/api/render-message) * [Render a message](/api/render-message)
* [Get a message's raw markdown](/api/get-raw-message) * [Get a message's raw Markdown](/api/get-raw-message)
* [Check messages match narrow](/api/check-narrow-matches) * [Check messages match narrow](/api/check-narrow-matches)
* [Get a message's edit history](/api/get-message-history) * [Get a message's edit history](/api/get-message-history)
* [Update personal message flags](/api/update-message-flags) * [Update personal message flags](/api/update-message-flags)

View File

@ -63,7 +63,7 @@ below by sending email to
* **.prefer-html**: The body of an email is typically encoded using * **.prefer-html**: The body of an email is typically encoded using
one or both of two common formats: plain text (`text/plain`) and one or both of two common formats: plain text (`text/plain`) and
HTML (`text/html`). Zulip supports constructing the Zulip message HTML (`text/html`). Zulip supports constructing the Zulip message
content using either (converting HTML to markdown for the HTML content using either (converting HTML to Markdown for the HTML
format). By default, Zulip will prefer using the plain text version format). By default, Zulip will prefer using the plain text version
of an email over the converted HTML version if both are present. of an email over the converted HTML version if both are present.
This option overrides that behavior to prefer the HTML version This option overrides that behavior to prefer the HTML version

View File

@ -93,7 +93,7 @@ EOF
./tools/update-prod-static ./tools/update-prod-static
# We don't need duplicate copies of emoji with hashed paths, and they would break markdown # We don't need duplicate copies of emoji with hashed paths, and they would break Markdown
find prod-static/serve/generated/emoji/images/emoji/ -regex '.*\.[0-9a-f]+\.png' -delete find prod-static/serve/generated/emoji/images/emoji/ -regex '.*\.[0-9a-f]+\.png' -delete
echo "$GITID" > build_id echo "$GITID" > build_id

View File

@ -127,7 +127,7 @@ def check_html_templates(templates: Iterable[str], all_dups: bool, fix: bool) ->
IGNORE_FILES = [ IGNORE_FILES = [
# zephyr-mirror.html has some whitespace-dependent formatting # zephyr-mirror.html has some whitespace-dependent formatting
# for code blocks that prevent cleaning it. Might make sense # for code blocks that prevent cleaning it. Might make sense
# to convert it to a /help/ markdown article. # to convert it to a /help/ Markdown article.
'templates/corporate/zephyr-mirror.html', 'templates/corporate/zephyr-mirror.html',
# Can't clean this because of `preserve_spaces` # Can't clean this because of `preserve_spaces`
'templates/zerver/app/markdown_help.html', 'templates/zerver/app/markdown_help.html',

View File

@ -35,6 +35,7 @@ IGNORED_PHRASES = [
r"LDAP", r"LDAP",
r"Mac", r"Mac",
r"macOS", r"macOS",
r"Markdown",
r"MiB", r"MiB",
r"OAuth", r"OAuth",
r"OTP", r"OTP",

View File

@ -76,7 +76,7 @@ comma_whitespace_rule: List["Rule"] = [
'bad_lines': ['foo(1, 2, 3)', 'foo(1, 2, 3)']}, 'bad_lines': ['foo(1, 2, 3)', 'foo(1, 2, 3)']},
] ]
markdown_whitespace_rules = list([rule for rule in whitespace_rules if rule['pattern'] != r'\s+$']) + [ markdown_whitespace_rules = list([rule for rule in whitespace_rules if rule['pattern'] != r'\s+$']) + [
# Two spaces trailing a line with other content is okay--it's a markdown line break. # Two spaces trailing a line with other content is okay--it's a Markdown line break.
# This rule finds one space trailing a non-space, three or more trailing spaces, and # This rule finds one space trailing a non-space, three or more trailing spaces, and
# spaces on an empty line. # spaces on an empty line.
{'pattern': r'((?<!\s)\s$)|(\s\s\s+$)|(^\s+$)', {'pattern': r'((?<!\s)\s$)|(\s\s\s+$)|(^\s+$)',
@ -648,7 +648,7 @@ markdown_rules = RuleList(
langs=['md'], langs=['md'],
rules=markdown_whitespace_rules + prose_style_rules + [ rules=markdown_whitespace_rules + prose_style_rules + [
{'pattern': r'\[(?P<url>[^\]]+)\]\((?P=url)\)', {'pattern': r'\[(?P<url>[^\]]+)\]\((?P=url)\)',
'description': 'Linkified markdown URLs should use cleaner <http://example.com> syntax.'}, 'description': 'Linkified Markdown URLs should use cleaner <http://example.com> syntax.'},
{'pattern': 'https://zulip.readthedocs.io/en/latest/[a-zA-Z0-9]', {'pattern': 'https://zulip.readthedocs.io/en/latest/[a-zA-Z0-9]',
'exclude': {'docs/overview/contributing.md', 'docs/overview/readme.md', 'docs/README.md'}, 'exclude': {'docs/overview/contributing.md', 'docs/overview/readme.md', 'docs/README.md'},
'include_only': {'docs/'}, 'include_only': {'docs/'},

View File

@ -322,7 +322,7 @@ def setup_old_emoji_farm(cache_path: str,
def generate_map_files(cache_path: str, emoji_catalog: Dict[str, List[str]]) -> None: def generate_map_files(cache_path: str, emoji_catalog: Dict[str, List[str]]) -> None:
# This function generates the main data file about emoji that are # This function generates the main data file about emoji that are
# consumed by the webapp, mobile apps, markdown processor, etc. # consumed by the webapp, mobile apps, Markdown processor, etc.
names = emoji_names_for_picker(EMOJI_NAME_MAPS) names = emoji_names_for_picker(EMOJI_NAME_MAPS)
codepoint_to_name = generate_codepoint_to_name_map(EMOJI_NAME_MAPS) codepoint_to_name = generate_codepoint_to_name_map(EMOJI_NAME_MAPS)
name_to_codepoint = generate_name_to_codepoint_map(EMOJI_NAME_MAPS) name_to_codepoint = generate_name_to_codepoint_map(EMOJI_NAME_MAPS)

View File

@ -128,7 +128,7 @@ def get_user_mentions(token: str, users: List[ZerverFieldsT],
return token, user_id return token, user_id
return token, None return token, None
# Map italic, bold and strikethrough markdown # Map italic, bold and strikethrough Markdown
def convert_markdown_syntax(text: str, regex: str, zulip_keyword: str) -> str: def convert_markdown_syntax(text: str, regex: str, zulip_keyword: str) -> str:
""" """
Returns: Returns:

View File

@ -1197,7 +1197,7 @@ def get_recipient_info(recipient: Recipient,
message_to_user_id_set = set(message_to_user_ids) message_to_user_id_set = set(message_to_user_ids)
user_ids = set(message_to_user_id_set) user_ids = set(message_to_user_id_set)
# Important note: Because we haven't rendered markdown yet, we # Important note: Because we haven't rendered Markdown yet, we
# don't yet know which of these possibly-mentioned users was # don't yet know which of these possibly-mentioned users was
# actually mentioned in the message (in other words, the # actually mentioned in the message (in other words, the
# mention syntax might have been in a code block or otherwise # mention syntax might have been in a code block or otherwise
@ -1271,7 +1271,7 @@ def get_recipient_info(recipient: Recipient,
# mentioned in it, and so can't use get_ids_for. # mentioned in it, and so can't use get_ids_for.
# #
# Further in the do_send_messages code path, once # Further in the do_send_messages code path, once
# `mentioned_user_ids` has been computed via markdown, we'll filter # `mentioned_user_ids` has been computed via Markdown, we'll filter
# these data structures for just those users who are either a # these data structures for just those users who are either a
# direct recipient or were mentioned; for now, we're just making # direct recipient or were mentioned; for now, we're just making
# sure we have the data we need for that without extra database # sure we have the data we need for that without extra database
@ -1676,7 +1676,7 @@ def create_user_messages(message: Message,
ums_to_create.append(um) ums_to_create.append(um)
# These properties on the Message are set via # These properties on the Message are set via
# render_markdown by code in the markdown inline patterns # render_markdown by code in the Markdown inline patterns
wildcard = message.mentions_wildcard wildcard = message.mentions_wildcard
ids_with_alert_words = message.user_ids_with_alert_words ids_with_alert_words = message.user_ids_with_alert_words
@ -4522,7 +4522,7 @@ def do_update_message(user_profile: UserProfile, message: Message,
event['is_me_message'] = Message.is_status_message(content, rendered_content) event['is_me_message'] = Message.is_status_message(content, rendered_content)
# message.has_image and message.has_link will have been # message.has_image and message.has_link will have been
# already updated by markdown rendering in the caller. # already updated by Markdown rendering in the caller.
message.has_attachment = check_attachment_reference_change(message) message.has_attachment = check_attachment_reference_change(message)
if message.is_stream_message(): if message.is_stream_message():

View File

@ -70,7 +70,7 @@ def relative_to_full_url(base_url: str, content: str) -> str:
container.drop_tree() container.drop_tree()
# The previous block handles most inline images, but for messages # The previous block handles most inline images, but for messages
# where the entire markdown input was just the URL of an image # where the entire Markdown input was just the URL of an image
# (i.e. the entire body is a message_inline_image object), the # (i.e. the entire body is a message_inline_image object), the
# entire message body will be that image element; here, we need a # entire message body will be that image element; here, we need a
# more drastic edit to the content. # more drastic edit to the content.

View File

@ -175,7 +175,7 @@ class InvalidMarkdownIncludeStatement(JsonableError):
@staticmethod @staticmethod
def msg_format() -> str: def msg_format() -> str:
return _("Invalid markdown include statement: {include_statement}") return _("Invalid Markdown include statement: {include_statement}")
class RateLimited(Exception): class RateLimited(Exception):
def __init__(self, msg: str="") -> None: def __init__(self, msg: str="") -> None:

View File

@ -259,7 +259,7 @@ def fix_message_rendered_content(realm: Realm,
for message in messages: for message in messages:
if message['rendered_content'] is not None: if message['rendered_content'] is not None:
# For Zulip->Zulip imports, we use the original rendered # For Zulip->Zulip imports, we use the original rendered
# markdown; this avoids issues where e.g. a mention can no # Markdown; this avoids issues where e.g. a mention can no
# longer render properly because a user has changed their # longer render properly because a user has changed their
# name. # name.
# #
@ -329,11 +329,11 @@ def fix_message_rendered_content(realm: Realm,
message['rendered_content_version'] = markdown_version message['rendered_content_version'] = markdown_version
except Exception: except Exception:
# This generally happens with two possible causes: # This generally happens with two possible causes:
# * rendering markdown throwing an uncaught exception # * rendering Markdown throwing an uncaught exception
# * rendering markdown failing with the exception being # * rendering Markdown failing with the exception being
# caught in markdown (which then returns None, causing the the # caught in Markdown (which then returns None, causing the the
# rendered_content assert above to fire). # rendered_content assert above to fire).
logging.warning("Error in markdown rendering for message ID %s; continuing", message['id']) logging.warning("Error in Markdown rendering for message ID %s; continuing", message['id'])
def current_table_ids(data: TableData, table: TableName) -> List[int]: def current_table_ids(data: TableData, table: TableName) -> List[int]:
""" """
@ -1218,7 +1218,7 @@ def import_message_data(realm: Realm,
sender_map=sender_map, sender_map=sender_map,
messages=data['zerver_message'], messages=data['zerver_message'],
) )
logging.info("Successfully rendered markdown for message batch") logging.info("Successfully rendered Markdown for message batch")
# A LOT HAPPENS HERE. # A LOT HAPPENS HERE.
# This is where we actually import the message data. # This is where we actually import the message data.

View File

@ -1,5 +1,5 @@
# Zulip's main markdown implementation. See docs/subsystems/markdown.md for # Zulip's main Markdown implementation. See docs/subsystems/markdown.md for
# detailed documentation on our markdown syntax. # detailed documentation on our Markdown syntax.
import datetime import datetime
import functools import functools
import html import html
@ -102,7 +102,7 @@ class LinkInfo(TypedDict):
DbData = Dict[str, Any] DbData = Dict[str, Any]
# Format version of the markdown rendering; stored along with rendered # Format version of the Markdown rendering; stored along with rendered
# messages so that we can efficiently determine what needs to be re-rendered # messages so that we can efficiently determine what needs to be re-rendered
version = 1 version = 1
@ -1124,7 +1124,7 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
unique_previewable_urls = {found_url.result[0] for found_url in found_urls unique_previewable_urls = {found_url.result[0] for found_url in found_urls
if not found_url.family.in_blockquote} if not found_url.family.in_blockquote}
# Set has_link and similar flags whenever a message is processed by markdown # Set has_link and similar flags whenever a message is processed by Markdown
if self.md.zulip_message: if self.md.zulip_message:
self.md.zulip_message.has_link = len(found_urls) > 0 self.md.zulip_message.has_link = len(found_urls) > 0
self.md.zulip_message.has_image = False # This is updated in self.add_a self.md.zulip_message.has_image = False # This is updated in self.add_a
@ -2096,7 +2096,7 @@ def maybe_update_markdown_engines(realm_filters_key: Optional[int], email_gatewa
if realm_filters_key not in realm_filter_data or \ if realm_filters_key not in realm_filter_data or \
realm_filter_data[realm_filters_key] != realm_filters: realm_filter_data[realm_filters_key] != realm_filters:
# Realm filters data has changed, update `realm_filter_data` and any # Realm filters data has changed, update `realm_filter_data` and any
# of the existing markdown engines using this set of realm filters. # of the existing Markdown engines using this set of realm filters.
realm_filter_data[realm_filters_key] = realm_filters realm_filter_data[realm_filters_key] = realm_filters
for email_gateway_flag in [True, False]: for email_gateway_flag in [True, False]:
if (realm_filters_key, email_gateway_flag) in md_engines: if (realm_filters_key, email_gateway_flag) in md_engines:
@ -2257,7 +2257,7 @@ def do_convert(content: str,
# This logic is a bit convoluted, but the overall goal is to support a range of use cases: # This logic is a bit convoluted, but the overall goal is to support a range of use cases:
# * Nothing is passed in other than content -> just run default options (e.g. for docs) # * Nothing is passed in other than content -> just run default options (e.g. for docs)
# * message is passed, but no realm is -> look up realm from message # * message is passed, but no realm is -> look up realm from message
# * message_realm is passed -> use that realm for markdown purposes # * message_realm is passed -> use that realm for Markdown purposes
if message is not None: if message is not None:
if message_realm is None: if message_realm is None:
message_realm = message.get_realm() message_realm = message.get_realm()
@ -2300,7 +2300,7 @@ def do_convert(content: str,
_md_engine.url_embed_preview_enabled = url_embed_preview_enabled( _md_engine.url_embed_preview_enabled = url_embed_preview_enabled(
message, message_realm, no_previews) message, message_realm, no_previews)
# Pre-fetch data from the DB that is used in the markdown thread # Pre-fetch data from the DB that is used in the Markdown thread
if message_realm is not None: if message_realm is not None:
# Here we fetch the data structures needed to render # Here we fetch the data structures needed to render
@ -2332,7 +2332,7 @@ def do_convert(content: str,
try: try:
# Spend at most 5 seconds rendering; this protects the backend # Spend at most 5 seconds rendering; this protects the backend
# from being overloaded by bugs (e.g. markdown logic that is # from being overloaded by bugs (e.g. Markdown logic that is
# extremely inefficient in corner cases) as well as user # extremely inefficient in corner cases) as well as user
# errors (e.g. a realm filter that makes some syntax # errors (e.g. a realm filter that makes some syntax
# infinite-loop). # infinite-loop).

View File

@ -10,7 +10,7 @@ def diff_strings(output: str, expected_output: str) -> str:
mdiff_path = "frontend_tests/zjsunit/mdiff.js" mdiff_path = "frontend_tests/zjsunit/mdiff.js"
if not os.path.isfile(mdiff_path): # nocoverage if not os.path.isfile(mdiff_path): # nocoverage
msg = "Cannot find mdiff for markdown diff rendering" msg = "Cannot find mdiff for Markdown diff rendering"
logging.error(msg) logging.error(msg)
raise DiffException(msg) raise DiffException(msg)

View File

@ -399,15 +399,15 @@ class MessageDict:
if Message.need_to_render_content(rendered_content, rendered_content_version, markdown_version): if Message.need_to_render_content(rendered_content, rendered_content_version, markdown_version):
# We really shouldn't be rendering objects in this method, but there is # We really shouldn't be rendering objects in this method, but there is
# a scenario where we upgrade the version of markdown and fail to run # a scenario where we upgrade the version of Markdown and fail to run
# management commands to re-render historical messages, and then we # management commands to re-render historical messages, and then we
# need to have side effects. This method is optimized to not need full # need to have side effects. This method is optimized to not need full
# blown ORM objects, but the markdown renderer is unfortunately highly # blown ORM objects, but the Markdown renderer is unfortunately highly
# coupled to Message, and we also need to persist the new rendered content. # coupled to Message, and we also need to persist the new rendered content.
# If we don't have a message object passed in, we get one here. The cost # If we don't have a message object passed in, we get one here. The cost
# of going to the DB here should be overshadowed by the cost of rendering # of going to the DB here should be overshadowed by the cost of rendering
# and updating the row. # and updating the row.
# TODO: see #1379 to eliminate markdown dependencies # TODO: see #1379 to eliminate Markdown dependencies
message = Message.objects.select_related().get(id=message_id) message = Message.objects.select_related().get(id=message_id)
assert message is not None # Hint for mypy. assert message is not None # Hint for mypy.
@ -689,7 +689,7 @@ def do_render_markdown(message: Message,
realm_alert_words_automaton: Optional[ahocorasick.Automaton]=None, realm_alert_words_automaton: Optional[ahocorasick.Automaton]=None,
mention_data: Optional[MentionData]=None, mention_data: Optional[MentionData]=None,
email_gateway: bool=False) -> str: email_gateway: bool=False) -> str:
"""Return HTML for given markdown. Markdown may add properties to the """Return HTML for given Markdown. Markdown may add properties to the
message object such as `mentions_user_ids`, `mentions_user_group_ids`, and message object such as `mentions_user_ids`, `mentions_user_group_ids`, and
`mentions_wildcard`. These are only on this Django object and are not `mentions_wildcard`. These are only on this Django object and are not
saved in the database. saved in the database.
@ -702,7 +702,7 @@ def do_render_markdown(message: Message,
message.links_for_preview = set() message.links_for_preview = set()
message.user_ids_with_alert_words = set() message.user_ids_with_alert_words = set()
# DO MAIN WORK HERE -- call markdown to convert # DO MAIN WORK HERE -- call markdown_convert to convert
rendered_content = markdown_convert( rendered_content = markdown_convert(
content, content,
realm_alert_words_automaton=realm_alert_words_automaton, realm_alert_words_automaton=realm_alert_words_automaton,

View File

@ -166,7 +166,7 @@ def send_response_message(bot_id: int, message_info: Dict[str, Any], response_da
topic - see get_topic_from_message_info topic - see get_topic_from_message_info
response_data is what the bot wants to send back and has these fields: response_data is what the bot wants to send back and has these fields:
content - raw markdown content for Zulip to render content - raw Markdown content for Zulip to render
""" """
message_type = message_info['type'] message_type = message_info['type']

View File

@ -280,7 +280,7 @@ def send_custom_email(users: List[UserProfile], options: Dict[str, Any]) -> None
subject_path = f"templates/{email_id}.subject.txt" subject_path = f"templates/{email_id}.subject.txt"
os.makedirs(os.path.dirname(html_source_template_path), exist_ok=True) os.makedirs(os.path.dirname(html_source_template_path), exist_ok=True)
# First, we render the markdown input file just like our # First, we render the Markdown input file just like our
# user-facing docs with render_markdown_path. # user-facing docs with render_markdown_path.
with open(plain_text_template_path, "w") as f: with open(plain_text_template_path, "w") as f:
f.write(parsed_email_template.get_payload()) f.write(parsed_email_template.get_payload())

View File

@ -177,11 +177,11 @@ on technical design issues.
what if you just want to look at something and go back to where you were? what if you just want to look at something and go back to where you were?
As a first step, I think it is viable to show the raw markdown, and then As a first step, I think it is viable to show the raw Markdown, and then
replace it when the server sends the update event. replace it when the server sends the update event.
Hmmm, I would not say the current implementation would handle all the math Hmmm, I would not say the current implementation would handle all the math
and the complex markdown, but it does pass all the tests in that file and the complex Markdown, but it does pass all the tests in that file
Wait, is this from the frontend js code or backend python code Wait, is this from the frontend js code or backend python code

View File

@ -51,7 +51,7 @@ timezone_data = None
# _calculate_timezones takes about 25ms to run, so we want to cache # _calculate_timezones takes about 25ms to run, so we want to cache
# its results (while avoiding running it on process startup since we # its results (while avoiding running it on process startup since we
# only need it for markdown rendering). # only need it for Markdown rendering).
def get_common_timezones() -> Dict[str, Union[int, Any]]: def get_common_timezones() -> Dict[str, Union[int, Any]]:
global timezone_data global timezone_data
if timezone_data is None: if timezone_data is None:

View File

@ -41,7 +41,7 @@ def check_full_name(full_name_raw: str) -> str:
character in UserProfile.NAME_INVALID_CHARS): character in UserProfile.NAME_INVALID_CHARS):
raise JsonableError(_("Invalid characters in name!")) raise JsonableError(_("Invalid characters in name!"))
# Names ending with e.g. `|15` could be ambiguous for # Names ending with e.g. `|15` could be ambiguous for
# sloppily-written parsers of our markdown syntax for mentioning # sloppily-written parsers of our Markdown syntax for mentioning
# users with ambiguous names, and likely have no real use, so we # users with ambiguous names, and likely have no real use, so we
# ban them. # ban them.
if re.search(r"\|\d+$", full_name_raw): if re.search(r"\|\d+$", full_name_raw):

View File

@ -94,7 +94,7 @@ class AdminNotifyHandler(logging.Handler):
# This parameter determines whether Zulip should attempt to # This parameter determines whether Zulip should attempt to
# send Zulip messages containing the error report. If there's # send Zulip messages containing the error report. If there's
# syntax that makes the markdown processor throw an exception, # syntax that makes the Markdown processor throw an exception,
# we really don't want to send that syntax into a new Zulip # we really don't want to send that syntax into a new Zulip
# message in exception handler (that's the stuff of which # message in exception handler (that's the stuff of which
# recursive exception loops are made). # recursive exception loops are made).

View File

@ -12,7 +12,7 @@ class Command(ZulipBaseCommand):
Useful to send a notice to all users of a realm or server. Useful to send a notice to all users of a realm or server.
The From and Subject headers can be provided in the body of the markdown The From and Subject headers can be provided in the body of the Markdown
document used to generate the email, or on the command line.""" document used to generate the email, or on the command line."""
def add_arguments(self, parser: ArgumentParser) -> None: def add_arguments(self, parser: ArgumentParser) -> None:
@ -22,13 +22,13 @@ class Command(ZulipBaseCommand):
dest='markdown_template_path', dest='markdown_template_path',
required=True, required=True,
type=str, type=str,
help='Path to a markdown-format body for the email.') help='Path to a Markdown-format body for the email.')
parser.add_argument('--subject', parser.add_argument('--subject',
type=str, type=str,
help='Subject for the email. It can be declared in markdown file in headers') help='Subject for the email. It can be declared in Markdown file in headers')
parser.add_argument('--from-name', parser.add_argument('--from-name',
type=str, type=str,
help='From line for the email. It can be declared in markdown file in headers') help='From line for the email. It can be declared in Markdown file in headers')
parser.add_argument('--reply-to', parser.add_argument('--reply-to',
type=str, type=str,
help='Optional reply-to line for the email') help='Optional reply-to line for the email')

View File

@ -739,7 +739,7 @@ def filter_format_validator(value: str) -> None:
class RealmFilter(models.Model): class RealmFilter(models.Model):
"""Realm-specific regular expressions to automatically linkify certain """Realm-specific regular expressions to automatically linkify certain
strings inside the markdown processor. See "Custom filters" in the settings UI. strings inside the Markdown processor. See "Custom filters" in the settings UI.
""" """
id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name='ID') id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name='ID')
realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE)

View File

@ -17,7 +17,7 @@ from zerver.openapi.curl_param_value_generators import (
def test_generated_curl_examples_for_success(client: Client) -> None: def test_generated_curl_examples_for_success(client: Client) -> None:
authentication_line = f"{client.email}:{client.api_key}" authentication_line = f"{client.email}:{client.api_key}"
# A limited markdown engine that just processes the code example syntax. # A limited Markdown engine that just processes the code example syntax.
realm = get_realm("zulip") realm = get_realm("zulip")
md_engine = markdown.Markdown(extensions=[markdown_extension.makeExtension( md_engine = markdown.Markdown(extensions=[markdown_extension.makeExtension(
api_url=realm.uri + "/api")]) api_url=realm.uri + "/api")])
@ -29,13 +29,13 @@ def test_generated_curl_examples_for_success(client: Client) -> None:
for file_name in sorted(glob.glob("templates/zerver/api/*.md")): for file_name in sorted(glob.glob("templates/zerver/api/*.md")):
documentation_lines = open(file_name).readlines() documentation_lines = open(file_name).readlines()
for line in documentation_lines: for line in documentation_lines:
# A typical example from the markdown source looks like this: # A typical example from the Markdown source looks like this:
# {generate_code_example(curl, ...} # {generate_code_example(curl, ...}
if not line.startswith("{generate_code_example(curl"): if not line.startswith("{generate_code_example(curl"):
continue continue
# To do an end-to-end test on the documentation examples # To do an end-to-end test on the documentation examples
# that will be actually shown to users, we use the # that will be actually shown to users, we use the
# markdown rendering pipeline to compute the user-facing # Markdown rendering pipeline to compute the user-facing
# example, and then run that to test it. # example, and then run that to test it.
curl_command_html = md_engine.convert(line.strip()) curl_command_html = md_engine.convert(line.strip())
unescaped_html = html.unescape(curl_command_html) unescaped_html = html.unescape(curl_command_html)

View File

@ -397,7 +397,7 @@ paths:
type: string type: string
description: | description: |
The `value` rendered in HTML. Will only be present for The `value` rendered in HTML. Will only be present for
custom profile field types that support markdown rendering. custom profile field types that support Markdown rendering.
This user-generated HTML content should be rendered This user-generated HTML content should be rendered
using the same CSS and client-side security protections using the same CSS and client-side security protections
@ -1855,7 +1855,7 @@ paths:
orig_content: orig_content:
type: string type: string
description: | description: |
The original markdown content of the message, before this edit. The original Markdown content of the message, before this edit.
orig_rendered_content: orig_rendered_content:
type: string type: string
description: | description: |
@ -1863,7 +1863,7 @@ paths:
prev_rendered_content_version: prev_rendered_content_version:
type: integer type: integer
description: | description: |
The markdown processor version number for the pre-edit message. The Markdown processor version number for the pre-edit message.
content: content:
type: string type: string
description: | description: |
@ -2288,7 +2288,7 @@ paths:
set of configured [Linkifiers](/help/add-a-custom-linkification-filter) set of configured [Linkifiers](/help/add-a-custom-linkification-filter)
for the organization has changed. for the organization has changed.
Processing this event is important to doing markdown local echo Processing this event is important to doing Markdown local echo
correctly. correctly.
properties: properties:
id: id:
@ -3106,7 +3106,7 @@ paths:
description: | description: |
If `true`, message content is returned in the rendered HTML If `true`, message content is returned in the rendered HTML
format. If `false`, message content is returned in the raw format. If `false`, message content is returned in the raw
markdown-format text that user entered. Markdown-format text that user entered.
schema: schema:
type: boolean type: boolean
default: true default: true
@ -3706,7 +3706,7 @@ paths:
This is a rarely-used endpoint relevant for clients that primarily This is a rarely-used endpoint relevant for clients that primarily
work with HTML-rendered messages but might need to occasionally fetch work with HTML-rendered messages but might need to occasionally fetch
the message's raw markdown (e.g. for pre-filling a message-editing the message's raw Markdown (e.g. for pre-filling a message-editing
UI). UI).
parameters: parameters:
- $ref: "#/components/parameters/MessageId" - $ref: "#/components/parameters/MessageId"
@ -8043,7 +8043,7 @@ paths:
data: data:
type: string type: string
description: | description: |
The message content, in raw markdown format (not rendered to HTML). The message content, in raw Markdown format (not rendered to HTML).
trigger: trigger:
type: string type: string
description: | description: |
@ -9175,7 +9175,7 @@ components:
maps the integer ID of a custom profile field in the organization to a maps the integer ID of a custom profile field in the organization to a
dictionary containing the user's data for that field. Generally the data dictionary containing the user's data for that field. Generally the data
includes just a single `value` key; for those custom profile fields includes just a single `value` key; for those custom profile fields
supporting markdown, a `rendered_value` key will also be present. supporting Markdown, a `rendered_value` key will also be present.
additionalProperties: additionalProperties:
type: object type: object
additionalProperties: false additionalProperties: false
@ -9191,7 +9191,7 @@ components:
type: string type: string
description: | description: |
The `value` rendered in HTML. Will only be present for The `value` rendered in HTML. Will only be present for
custom profile field types that support markdown rendering. custom profile field types that support Markdown rendering.
This user-generated HTML content should be rendered This user-generated HTML content should be rendered
using the same CSS and client-side security protections using the same CSS and client-side security protections

View File

@ -78,9 +78,9 @@ docs_without_macros = [
def render_markdown_path(markdown_file_path: str, def render_markdown_path(markdown_file_path: str,
context: Mapping[str, Any]={}, context: Mapping[str, Any]={},
pure_markdown: bool=False) -> str: pure_markdown: bool=False) -> str:
"""Given a path to a markdown file, return the rendered html. """Given a path to a Markdown file, return the rendered html.
Note that this assumes that any HTML in the markdown file is Note that this assumes that any HTML in the Markdown file is
trusted; it is intended to be used for documentation, not user trusted; it is intended to be used for documentation, not user
data.""" data."""
@ -136,7 +136,7 @@ def render_markdown_path(markdown_file_path: str,
jinja = engines['Jinja2'] jinja = engines['Jinja2']
try: try:
# By default, we do both Jinja2 templating and markdown # By default, we do both Jinja2 templating and Markdown
# processing on the file, to make it easy to use both Jinja2 # processing on the file, to make it easy to use both Jinja2
# context variables and markdown includes in the file. # context variables and markdown includes in the file.
markdown_string = jinja.env.loader.get_source(jinja.env, markdown_file_path)[0] markdown_string = jinja.env.loader.get_source(jinja.env, markdown_file_path)[0]

View File

@ -922,7 +922,7 @@ class MarkdownTest(ZulipTestCase):
realm = get_realm('zulip') realm = get_realm('zulip')
# Needs to mock an actual message because that's how markdown obtains the realm # Needs to mock an actual message because that's how Markdown obtains the realm
msg = Message(sender=self.example_user('hamlet')) msg = Message(sender=self.example_user('hamlet'))
converted = markdown_convert(":green_tick:", message_realm=realm, message=msg) converted = markdown_convert(":green_tick:", message_realm=realm, message=msg)
realm_emoji = RealmEmoji.objects.filter(realm=realm, realm_emoji = RealmEmoji.objects.filter(realm=realm,
@ -1046,7 +1046,7 @@ class MarkdownTest(ZulipTestCase):
assert_conversion('Hello #123World', False) assert_conversion('Hello #123World', False)
assert_conversion('Hello#123 World', False) assert_conversion('Hello#123 World', False)
assert_conversion('Hello#123World', False) assert_conversion('Hello#123World', False)
# Ideally, these should be converted, but markdown doesn't # Ideally, these should be converted, but Markdown doesn't
# handle word boundary detection in languages that don't use # handle word boundary detection in languages that don't use
# whitespace for that correctly yet. # whitespace for that correctly yet.
assert_conversion('チケットは#123です', False) assert_conversion('チケットは#123です', False)
@ -1229,7 +1229,7 @@ class MarkdownTest(ZulipTestCase):
content = """Hello, everyone. Prod deployment has been completed content = """Hello, everyone. Prod deployment has been completed
And this is a new line And this is a new line
to test out how markdown convert this into something line ending split array to test out how Markdown convert this into something line ending split array
and this is a new line and this is a new line
last""" last"""
render(msg, content) render(msg, content)
@ -1969,7 +1969,7 @@ class MarkdownTest(ZulipTestCase):
) )
def test_mit_rendering(self) -> None: def test_mit_rendering(self) -> None:
"""Test the markdown configs for the MIT Zephyr mirroring system; """Test the Markdown configs for the MIT Zephyr mirroring system;
verifies almost all inline patterns are disabled, but verifies almost all inline patterns are disabled, but
inline_interesting_links is still enabled""" inline_interesting_links is still enabled"""
msg = "**test**" msg = "**test**"

View File

@ -46,7 +46,7 @@ class MessageDictTest(ZulipTestCase):
Different clients have different needs Different clients have different needs
when it comes to things like generating avatar when it comes to things like generating avatar
hashes or including both rendered and unrendered hashes or including both rendered and unrendered
markdown, so that explains the different shapes. Markdown, so that explains the different shapes.
And then the two codepaths have different And then the two codepaths have different
performance needs. In the events codepath, we performance needs. In the events codepath, we

View File

@ -113,7 +113,7 @@ footer
'markdown_test_file': "zerver/tests/markdown/test_custom_include_extension.md", 'markdown_test_file': "zerver/tests/markdown/test_custom_include_extension.md",
} }
with self.assertRaisesRegex(InvalidMarkdownIncludeStatement, "Invalid markdown include statement"): with self.assertRaisesRegex(InvalidMarkdownIncludeStatement, "Invalid Markdown include statement"):
template.render(context) template.render(context)
self.assertEqual(mock_print.mock_calls, [ self.assertEqual(mock_print.mock_calls, [
call("Warning: could not find file templates/zerver/help/include/nonexistent-macro.md. Error: [Errno 2] No such file or directory: 'templates/zerver/help/include/nonexistent-macro.md'") call("Warning: could not find file templates/zerver/help/include/nonexistent-macro.md. Error: [Errno 2] No such file or directory: 'templates/zerver/help/include/nonexistent-macro.md'")

View File

@ -173,7 +173,7 @@ class ThumbnailTest(ZulipTestCase):
# Test full size image. # Test full size image.
# We remove the forward slash infront of the `/user_uploads/` to match # We remove the forward slash infront of the `/user_uploads/` to match
# markdown behaviour. # Markdown behaviour.
quoted_uri = urllib.parse.quote(uri[1:], safe='') quoted_uri = urllib.parse.quote(uri[1:], safe='')
result = self.client_get(f"/thumbnail?url={quoted_uri}&size=full") result = self.client_get(f"/thumbnail?url={quoted_uri}&size=full")
self.assertEqual(result.status_code, 302, result) self.assertEqual(result.status_code, 302, result)
@ -197,7 +197,7 @@ class ThumbnailTest(ZulipTestCase):
uri = json["uri"] uri = json["uri"]
# We remove the forward slash infront of the `/user_uploads/` to match # We remove the forward slash infront of the `/user_uploads/` to match
# markdown behaviour. # Markdown behaviour.
quoted_uri = urllib.parse.quote(uri[1:], safe='') quoted_uri = urllib.parse.quote(uri[1:], safe='')
result = self.client_get(f"/thumbnail?url={quoted_uri}&size=full") result = self.client_get(f"/thumbnail?url={quoted_uri}&size=full")
self.assertEqual(result.status_code, 302, result) self.assertEqual(result.status_code, 302, result)

View File

@ -338,7 +338,7 @@ class PermissionTest(ZulipTestCase):
self.assert_json_error(result, 'Name too short!') self.assert_json_error(result, 'Name too short!')
def test_not_allowed_format(self) -> None: def test_not_allowed_format(self) -> None:
# Name of format "Alice|999" breaks in markdown # Name of format "Alice|999" breaks in Markdown
new_name = 'iago|72' new_name = 'iago|72'
self.login('iago') self.login('iago')
req = dict(full_name=ujson.dumps(new_name)) req = dict(full_name=ujson.dumps(new_name))
@ -346,7 +346,7 @@ class PermissionTest(ZulipTestCase):
self.assert_json_error(result, 'Invalid format!') self.assert_json_error(result, 'Invalid format!')
def test_allowed_format_complex(self) -> None: def test_allowed_format_complex(self) -> None:
# Adding characters after r'|d+' doesn't break markdown # Adding characters after r'|d+' doesn't break Markdown
new_name = 'Hello- 12iago|72k' new_name = 'Hello- 12iago|72k'
self.login('iago') self.login('iago')
req = dict(full_name=ujson.dumps(new_name)) req = dict(full_name=ujson.dumps(new_name))

View File

@ -26,7 +26,7 @@ def api_appfollow_webhook(request: HttpRequest, user_profile: UserProfile,
return json_success() return json_success()
def convert_markdown(text: str) -> str: def convert_markdown(text: str) -> str:
# Converts Slack-style markdown to Zulip format # Converts Slack-style Markdown to Zulip format
# Implemented mainly for AppFollow messages # Implemented mainly for AppFollow messages
# Not ready for general use as some edge-cases not handled # Not ready for general use as some edge-cases not handled
# Convert Bold # Convert Bold

View File

@ -95,7 +95,7 @@ Requester Bob <requester-bob@example.com> added a {} note to \
def test_inline_image(self) -> None: def test_inline_image(self) -> None:
""" """
Freshdesk sends us descriptions as HTML, so we have to make the Freshdesk sends us descriptions as HTML, so we have to make the
descriptions Zulip markdown-friendly while still doing our best to descriptions Zulip Markdown-friendly while still doing our best to
preserve links and images. preserve links and images.
""" """
expected_topic = "#12: Not enough ☃ guinea pigs" expected_topic = "#12: Not enough ☃ guinea pigs"

View File

@ -66,7 +66,7 @@ def convert_jira_markup(content: str, realm: Realm) -> str:
# In order to support both forms, we don't match a | in bare links # In order to support both forms, we don't match a | in bare links
content = re.sub(r'\[([^\|~]+?)\]', r'[\1](\1)', content) content = re.sub(r'\[([^\|~]+?)\]', r'[\1](\1)', content)
# Full links which have a | are converted into a better markdown link # Full links which have a | are converted into a better Markdown link
full_link_re = re.compile(r'\[(?:(?P<title>[^|~]+)\|)(?P<url>[^\]]*)\]') full_link_re = re.compile(r'\[(?:(?P<title>[^|~]+)\|)(?P<url>[^\]]*)\]')
content = re.sub(full_link_re, r'[\g<title>](\g<url>)', content) content = re.sub(full_link_re, r'[\g<title>](\g<url>)', content)

View File

@ -45,7 +45,7 @@ the following conditions** select **Ticket: is...** and then select
**Notification: Notify target**, then select **Zulip**. **Notification: Notify target**, then select **Zulip**.
Next we need need to enter the message body into Message. You can use Next we need need to enter the message body into Message. You can use
Zulip markdown and the Zendesk placeholders when creating your message. Zulip Markdown and the Zendesk placeholders when creating your message.
You can copy this example template: You can copy this example template:

View File

@ -445,7 +445,7 @@ ENABLE_GRAVATAR = True
#REMOTE_POSTGRES_SSLMODE = 'require' #REMOTE_POSTGRES_SSLMODE = 'require'
# If you want to set a Terms of Service for your server, set the path # If you want to set a Terms of Service for your server, set the path
# to your markdown file, and uncomment the following line. # to your Markdown file, and uncomment the following line.
#TERMS_OF_SERVICE = '/etc/zulip/terms.md' #TERMS_OF_SERVICE = '/etc/zulip/terms.md'
# Similarly if you want to set a Privacy Policy. # Similarly if you want to set a Privacy Policy.

View File

@ -4,4 +4,4 @@
put your terms here put your terms here
* you can use markdown * you can use Markdown