mirror of https://github.com/zulip/zulip.git
docs: Capitalize Markdown consistently.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
60a25b2721
commit
768f9f93cd
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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> >> Test for markdown preview</p>",
|
"<p><strong>Markdown Preview</strong> >> Test for Markdown preview</p>",
|
||||||
"Check markdown is previewed properly"
|
"Check Markdown is previewed properly"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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> >> Test for markdown preview</p>";
|
"<p><strong>Markdown Preview</strong> >> 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(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -11,5 +11,5 @@
|
||||||
sphinx
|
sphinx
|
||||||
sphinx-rtd-theme
|
sphinx-rtd-theme
|
||||||
|
|
||||||
# Needed to build markdown docs
|
# Needed to build Markdown docs
|
||||||
recommonmark
|
recommonmark
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
||||||
};
|
};
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 === "*") {
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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**
|
||||||
|
|
||||||
|
|
|
@ -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)}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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/'},
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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():
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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**"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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'")
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
put your terms here
|
put your terms here
|
||||||
|
|
||||||
|
|
||||||
* you can use markdown
|
* you can use Markdown
|
||||||
|
|
Loading…
Reference in New Issue