mirror of https://github.com/zulip/zulip.git
docs: Fix capitalization mistakes.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
41e8872b0f
commit
544bbd5398
|
@ -75,7 +75,7 @@ zulip.kdev4
|
|||
*.sublime-workspace
|
||||
.vscode/
|
||||
*.DS_Store
|
||||
# .cache/ is generated by VSCode's test runner
|
||||
# .cache/ is generated by Visual Studio Code's test runner
|
||||
.cache/
|
||||
.eslintcache
|
||||
|
||||
|
|
|
@ -222,7 +222,7 @@ skills needed, and try to emphasize where strong skills with
|
|||
particular tools are likely to be important for a given project.
|
||||
|
||||
For all of our projects, an important skill to develop is a good
|
||||
command of Git; read [our Git Guide](../git/overview.md) in full to
|
||||
command of Git; read [our Git guide](../git/overview.md) in full to
|
||||
learn how to use it well. Of particular importance is mastering using
|
||||
Git rebase so that you can construct commits that are clearly correct
|
||||
and explain why they are correct. We highly recommend investing in
|
||||
|
|
|
@ -133,7 +133,7 @@ don't have a favorite, here are some suggestions:
|
|||
* [sublime](https://www.sublimetext.com/)
|
||||
* [PyCharm](https://www.jetbrains.com/pycharm/)
|
||||
|
||||
Next, follow our [Git and GitHub Guide](../git/index.md) to clone and configure
|
||||
Next, follow our [Git and GitHub guide](../git/index.md) to clone and configure
|
||||
your fork of zulip on your local computer.
|
||||
|
||||
Once you have cloned your code locally, you can get to work.
|
||||
|
@ -145,7 +145,7 @@ is to **push them to GitHub** and then **fetch and merge** them from
|
|||
the remote server.
|
||||
|
||||
For more detailed instructions about how to do this, see our [Git & GitHub
|
||||
Guide][rtd-git-guide]. In brief, the steps are as follows.
|
||||
guide][rtd-git-guide]. In brief, the steps are as follows.
|
||||
|
||||
On your **local computer**:
|
||||
|
||||
|
@ -184,7 +184,7 @@ you prefer for development in general.
|
|||
|
||||
If you use [TextMate](https://macromates.com), Atom, VS Code, or a
|
||||
similar GUI editor, tools like
|
||||
[VSCode Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh) and
|
||||
[Visual Studio Code Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh) and
|
||||
[rmate](https://github.com/textmate/rmate) that are designed to
|
||||
integrate that editor with remote development over SSH allow you to
|
||||
develop remotely from the comfort of your local machine.
|
||||
|
@ -264,7 +264,7 @@ Now your workspace should look similar this:
|
|||
|
||||
Next, read the following to learn more about developing for Zulip:
|
||||
|
||||
* [Git & GitHub Guide][rtd-git-guide]
|
||||
* [Git & GitHub guide][rtd-git-guide]
|
||||
* [Using the development environment][rtd-using-dev-env]
|
||||
* [Testing][rtd-testing]
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ development environment already set up.
|
|||
The machines (droplets) are being generously provided by
|
||||
[DigitalOcean](https://www.digitalocean.com/). Thank you DigitalOcean!
|
||||
|
||||
## Step 1: Join GitHub and create SSH Keys
|
||||
## Step 1: Join GitHub and create SSH keys
|
||||
|
||||
To contribute to Zulip and to use a remote Zulip developer instance, you'll
|
||||
need a GitHub account. If you don't already have one, sign up
|
||||
|
@ -72,7 +72,7 @@ Once you've confirmed you can connect to your remote server, take a look at:
|
|||
|
||||
* [developing remotely](../development/remote.md) for tips on using the remote dev
|
||||
instance, and
|
||||
* our [Git & GitHub Guide](../git/index.md) to learn how to use Git with Zulip.
|
||||
* our [Git & GitHub guide](../git/index.md) to learn how to use Git with Zulip.
|
||||
|
||||
Next, read the following to learn more about developing for Zulip:
|
||||
|
||||
|
|
|
@ -308,7 +308,7 @@ provisioning if your Internet connection is unreliable. To retry, you
|
|||
can use `vagrant provision` (`vagrant up` will just boot the guest
|
||||
without provisioning after the first time). Other common issues are
|
||||
documented in the
|
||||
[Troubleshooting and Common Errors](#troubleshooting-and-common-errors)
|
||||
[Troubleshooting and common errors](#troubleshooting-and-common-errors)
|
||||
section. If that doesn't help, please visit
|
||||
[#provision help](https://chat.zulip.org/#narrow/stream/21-provision-help)
|
||||
in the [Zulip development community server](../contributing/chat-zulip-org.md) for
|
||||
|
@ -432,7 +432,7 @@ running Git commands in Terminal (macOS/Ubuntu) or Git BASH (Windows) in the
|
|||
directory where you cloned Zulip on your main machine.
|
||||
|
||||
If you're new to working with Git/GitHub, check out our [Git & GitHub
|
||||
Guide][rtd-git-guide].
|
||||
guide][rtd-git-guide].
|
||||
|
||||
#### Maintaining the development environment
|
||||
|
||||
|
@ -533,7 +533,7 @@ $ ./tools/run-dev.py
|
|||
|
||||
Next, read the following to learn more about developing for Zulip:
|
||||
|
||||
* [Git & GitHub Guide][rtd-git-guide]
|
||||
* [Git & GitHub guide][rtd-git-guide]
|
||||
* [Using the development environment][rtd-using-dev-env]
|
||||
* [Testing][rtd-testing] (and [Configuring CI][ci] to
|
||||
run the full test suite against any branches you push to your fork,
|
||||
|
|
|
@ -31,7 +31,7 @@ These three systems are documented in detail.
|
|||
What you are reading right now is part of the collection of
|
||||
documentation targeted at developers and people running their own
|
||||
Zulip servers. These docs are written in
|
||||
[Commonmark Markdown](https://commonmark.org/) with a small bit of rST.
|
||||
[CommonMark Markdown](https://commonmark.org/) with a small bit of rST.
|
||||
We've chosen Markdown because it is
|
||||
[easy to write](https://commonmark.org/help/). The source for Zulip's
|
||||
developer documentation is at `docs/` in the Zulip Git repository, and
|
||||
|
|
|
@ -4,7 +4,7 @@ This is mostly from
|
|||
|
||||
## Fixing the last commit
|
||||
### Changing the last commit message
|
||||
1. `git commit --amend -m "New Message"`
|
||||
1. `git commit --amend -m "New message"`
|
||||
|
||||
### Changing the last commit
|
||||
1. Make your changes to the files
|
||||
|
|
|
@ -45,7 +45,7 @@ extremely reliable for years, whereas the Docker image is new and has
|
|||
rough edges, so we recommend the normal installer unless you have a
|
||||
specific reason to prefer Docker.
|
||||
|
||||
## Advanced Installer Options
|
||||
## Advanced installer options
|
||||
|
||||
The Zulip installer supports the following advanced installer options
|
||||
as well as those mentioned in the
|
||||
|
|
|
@ -11,7 +11,7 @@ To enable this integration, you need to get a production API key from
|
|||
|
||||
1. [Create a GIPHY account](https://giphy.com/join).
|
||||
|
||||
1. Create a GIPHY API Key by clicking “Create an App” on the
|
||||
1. Create a GIPHY API key by clicking “Create an App” on the
|
||||
[Developer Dashboard][giphy-dashboard].
|
||||
|
||||
1. Choose **SDK** as product type and click **Next Step**.
|
||||
|
|
|
@ -12,7 +12,7 @@ Moved to [Troubleshooting](../production/troubleshooting.html#monitoring).
|
|||
|
||||
### Securing your Zulip server
|
||||
|
||||
Moved to [Security Model](../production/security-model.md).
|
||||
Moved to [Security model](../production/security-model.md).
|
||||
|
||||
### Upgrading
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ client. This approach is reasonably efficient and works everywhere
|
|||
(unlike websockets, which have a decreasing but nonzero level of
|
||||
client compatibility problems).
|
||||
|
||||
For each connected client, the **Event Queue Server** maintains an
|
||||
For each connected client, the **event queue server** maintains an
|
||||
**event queue**, which contains any events that are to be delivered to
|
||||
that client which have not yet been acknowledged by that client.
|
||||
Ignoring the subtle details around error handling, the protocol is
|
||||
|
@ -156,15 +156,15 @@ requesting events; its handler for this case should just restart the
|
|||
client / reload the browser so that it refetches initial data the same
|
||||
way it would on startup. Since clients have to implement their
|
||||
startup process anyway, this approach adds minimal technical
|
||||
complexity to clients. A nice side effect is that if the Event Queue
|
||||
Server (which stores queues in memory) were to crash and lose
|
||||
complexity to clients. A nice side effect is that if the event queue
|
||||
server (which stores queues in memory) were to crash and lose
|
||||
its data, clients would recover, just as if they had lost Internet
|
||||
access briefly (there is some DoS risk to manage, though).
|
||||
|
||||
Note that the garbage-collection system has hooks that are important
|
||||
for the implementation of [notifications](../subsystems/notifications.md).
|
||||
|
||||
(The Event Queue Server is designed to save any event queues to disk
|
||||
(The event queue server is designed to save any event queues to disk
|
||||
and reload them when the server is restarted, and catches exceptions
|
||||
carefully, so such incidents are very rare, but it's nice to have a
|
||||
design that handles them without leaving broken out-of-date clients
|
||||
|
|
|
@ -227,7 +227,7 @@ A few useful notes are:
|
|||
* Zulip installs static assets in production in
|
||||
`/home/zulip/prod-static`. When a new version is deployed, before the
|
||||
server is restarted, files are copied into that directory.
|
||||
* We use the VFL (Versioned File Layout) strategy, where each file in
|
||||
* We use the VFL (versioned file layout) strategy, where each file in
|
||||
the codebase (e.g. `favicon.ico`) gets a new name
|
||||
(e.g. `favicon.c55d45ae8c58.ico`) that contains a hash in it. Each
|
||||
deployment, has a manifest file
|
||||
|
|
|
@ -11,10 +11,10 @@ the details of the email/mobile push notifications code path.
|
|||
Here we name a few corner cases worth understanding in designing this
|
||||
sort of notifications system:
|
||||
|
||||
* The **Idle Desktop Problem**: We don't want the presence of a
|
||||
* The **idle desktop problem**: We don't want the presence of a
|
||||
desktop computer at the office to eat all notifications because the
|
||||
user has an "online" client that they may not have used in 3 days.
|
||||
* The **Hard Disconnect Problem**: A client can lose its connection to
|
||||
* The **hard disconnect problem**: A client can lose its connection to
|
||||
the Internet (or be suspended, or whatever) at any time, and this
|
||||
happens routinely. We want to ensure that races where a user closes
|
||||
their laptop shortly after a notifiable message is sent does not
|
||||
|
@ -57,10 +57,10 @@ as follows:
|
|||
checks are implemented.
|
||||
* Users in `presence_idle_user_ids` are always considered idle:
|
||||
the variable name means "users who are idle because of
|
||||
presence". This is how we solve the Idle Desktop Problem; users
|
||||
presence". This is how we solve the idle desktop problem; users
|
||||
with an idle desktop are treated the same as users who aren't
|
||||
logged in for this check.
|
||||
* However, that check does not handle the Hard Disconnect Problem:
|
||||
* However, that check does not handle the hard disconnect problem:
|
||||
if a user was present 1 minute before a message was sent, and then
|
||||
closed their laptop, the user will not be in
|
||||
`presence_idle_user_ids`, and so without an additional mechanism,
|
||||
|
@ -75,7 +75,7 @@ as follows:
|
|||
* The `receiver_is_off_zulip` check is effectively repeated when
|
||||
event queues are garbage-collected (in `missedmessage_hook`) by
|
||||
looking for whether the queue being garbage-collectee was the only
|
||||
one; this second check solves the Hard Disconnect Problem, resulting in
|
||||
one; this second check solves the hard disconnect problem, resulting in
|
||||
notifications for these hard-disconnect cases usually coming 10
|
||||
minutes late.
|
||||
* The message-edit code path has parallel logic in
|
||||
|
@ -124,8 +124,8 @@ as follows:
|
|||
`push_notifications.py` code that actually sends the
|
||||
notification. This logic is somewhat complicated by having to track
|
||||
the number of unread push notifications to display on the mobile
|
||||
apps' badges, as well as using the [Mobile Push Notifications
|
||||
Service](../production/mobile-push-notifications.md) for self-hosted
|
||||
apps' badges, as well as using the [mobile push notifications
|
||||
service](../production/mobile-push-notifications.md) for self-hosted
|
||||
systems.
|
||||
|
||||
The following important constraints are worth understanding about the
|
||||
|
|
|
@ -76,12 +76,12 @@ The remaining details in this section are primarily relevant for doing
|
|||
development on our CI system and/or provisioning process.
|
||||
|
||||
The first key of the job section is `docker`. The docker key specifies
|
||||
the image GitHub Action should get from [Docker Hub][docker-hub] for running
|
||||
the job. Once GitHub Action fetches the image from Docker Hub, it will spin
|
||||
the image GitHub Actions should get from [Docker Hub][docker-hub] for running
|
||||
the job. Once GitHub Actions fetches the image from Docker Hub, it will spin
|
||||
up a docker container. See [images](#images) section to know more about
|
||||
the images we use in GitHub Action for testing.
|
||||
the images we use in GitHub Actions for testing.
|
||||
|
||||
After booting the container from the configured image, GitHub Action will
|
||||
After booting the container from the configured image, GitHub Actions will
|
||||
create the directory mentioned in `working_directory` and all the
|
||||
steps are be run from here.
|
||||
|
||||
|
@ -92,7 +92,7 @@ are defined in the `aliases` section at the top of the file.
|
|||
|
||||
### Images
|
||||
|
||||
GitHub Action tests are run in containers that are spun off from the images
|
||||
GitHub Actions tests are run in containers that are spun off from the images
|
||||
maintained by Zulip team. The Dockerfiles for the various images can be
|
||||
generated by running `./tools/ci/generate-dockerfiles`. This command
|
||||
will generate the Dockerfiles of the three Ubuntu releases in
|
||||
|
@ -106,7 +106,7 @@ generated Dockerfiles.
|
|||
|
||||
#### Caching
|
||||
|
||||
An important element of making GitHub Action perform effectively is caching
|
||||
An important element of making GitHub Actions perform effectively is caching
|
||||
between jobs the various caches that live under `/srv/` in a Zulip
|
||||
development or production environment. In particular, we cache the
|
||||
following:
|
||||
|
@ -114,7 +114,7 @@ following:
|
|||
* Python virtualenvs
|
||||
* node_modules directories
|
||||
|
||||
This has a huge impact on the performance of running tests in GitHub Action
|
||||
This has a huge impact on the performance of running tests in GitHub Actions
|
||||
CI; without these caches, the average test time would be several times
|
||||
longer.
|
||||
|
||||
|
|
|
@ -63,8 +63,8 @@ to understand when implementing an internationalized application:
|
|||
find your string.
|
||||
|
||||
There's a lot of other interesting differences that are important for
|
||||
i18n (e.g. Zulip has a "Full Name" field rather than "First Name" and
|
||||
"Last Name" because different cultures order the surnames and given
|
||||
i18n (e.g. Zulip has a "full name" field rather than "first name" and
|
||||
"last name" because different cultures order the surnames and given
|
||||
names differently), but the above issues are likely to be relevant to
|
||||
most people working on Zulip.
|
||||
|
||||
|
@ -175,8 +175,8 @@ ensure this, the error message passed to `json_error` and
|
|||
function, e.g.:
|
||||
|
||||
```
|
||||
json_error(_('English Text'))
|
||||
JsonableError(_('English Text'))
|
||||
json_error(_('English text'))
|
||||
JsonableError(_('English text'))
|
||||
```
|
||||
|
||||
If you're declaring a user-facing string at top level or in a class, you need to
|
||||
|
@ -215,7 +215,7 @@ To mark a string translatable in JavaScript files, pass it to the
|
|||
`intl.formatMessage` function, which we alias to `$t` in `intl.js`:
|
||||
|
||||
```js
|
||||
$t({defaultMessage: "English Text"})
|
||||
$t({defaultMessage: "English text"})
|
||||
```
|
||||
|
||||
The string to be translated must be a constant literal string, but
|
||||
|
@ -258,14 +258,14 @@ For translations in Handlebars templates we also use FormatJS, through two
|
|||
Handlebars [helpers][] that Zulip registers. The syntax for simple strings is:
|
||||
|
||||
```
|
||||
{{t 'English Text' }}
|
||||
{{t 'English text' }}
|
||||
```
|
||||
|
||||
If you are passing a translated string to a Handlebars partial, you can use:
|
||||
|
||||
```
|
||||
{{> template_name
|
||||
variable_name=(t 'English Text')
|
||||
variable_name=(t 'English text')
|
||||
}}
|
||||
```
|
||||
|
||||
|
|
|
@ -198,7 +198,7 @@ return `json_error` in the case of an error, which gives a JSON string:
|
|||
`{'result': 'error', 'msg': <some error message>}`
|
||||
|
||||
in a
|
||||
[HTTP Response](https://docs.djangoproject.com/en/1.8/ref/request-response/)
|
||||
[HTTP response](https://docs.djangoproject.com/en/1.8/ref/request-response/)
|
||||
with a content type of 'application/json'.
|
||||
|
||||
To pass back data from the server to the calling client, in the event of
|
||||
|
|
|
@ -79,17 +79,17 @@ run_test("paste_handler", () => {
|
|||
assert.equal(copy_and_paste.paste_handler_converter(input), "*This text is italic*");
|
||||
|
||||
input =
|
||||
'<div class="preview-content"><div class="comment"><div class="comment-body markdown-body js-preview-body" style="min-height: 131px;"><p>Test List:</p><ul><li>Item 1</li><li>Item 2</li></ul></div></div></div>';
|
||||
'<div class="preview-content"><div class="comment"><div class="comment-body markdown-body js-preview-body" style="min-height: 131px;"><p>Test list:</p><ul><li>Item 1</li><li>Item 2</li></ul></div></div></div>';
|
||||
assert.equal(
|
||||
copy_and_paste.paste_handler_converter(input),
|
||||
"Test List:\n* Item 1\n* Item 2",
|
||||
"Test list:\n* Item 1\n* Item 2",
|
||||
);
|
||||
|
||||
input =
|
||||
'<div class="ace-line gutter-author-d-iz88z86z86za0dz67zz78zz78zz74zz68zjz80zz71z9iz90za3z66zs0z65zz65zq8z75zlaz81zcz66zj6g2mz78zz76zmz66z22z75zfcz69zz66z ace-ltr focused-line" dir="auto" id="editor-3-ace-line-41"><span>Test List:</span></div><div class="ace-line gutter-author-d-iz88z86z86za0dz67zz78zz78zz74zz68zjz80zz71z9iz90za3z66zs0z65zz65zq8z75zlaz81zcz66zj6g2mz78zz76zmz66z22z75zfcz69zz66z line-list-type-bullet ace-ltr" dir="auto" id="editor-3-ace-line-42"><ul class="listtype-bullet listindent1 list-bullet1"><li><span class="ace-line-pocket-zws" data-faketext="" data-contentcollector-ignore-space-at="end"></span><span class="ace-line-pocket" data-faketext="" contenteditable="false"></span><span class="ace-line-pocket-zws" data-faketext="" data-contentcollector-ignore-space-at="start"></span><span>Item 1</span></li></ul></div><div class="ace-line gutter-author-d-iz88z86z86za0dz67zz78zz78zz74zz68zjz80zz71z9iz90za3z66zs0z65zz65zq8z75zlaz81zcz66zj6g2mz78zz76zmz66z22z75zfcz69zz66z line-list-type-bullet ace-ltr" dir="auto" id="editor-3-ace-line-43"><ul class="listtype-bullet listindent1 list-bullet1"><li><span class="ace-line-pocket-zws" data-faketext="" data-contentcollector-ignore-space-at="end"></span><span class="ace-line-pocket" data-faketext="" contenteditable="false"></span><span class="ace-line-pocket-zws" data-faketext="" data-contentcollector-ignore-space-at="start"></span><span>Item 2</span></li></ul></div>';
|
||||
'<div class="ace-line gutter-author-d-iz88z86z86za0dz67zz78zz78zz74zz68zjz80zz71z9iz90za3z66zs0z65zz65zq8z75zlaz81zcz66zj6g2mz78zz76zmz66z22z75zfcz69zz66z ace-ltr focused-line" dir="auto" id="editor-3-ace-line-41"><span>Test list:</span></div><div class="ace-line gutter-author-d-iz88z86z86za0dz67zz78zz78zz74zz68zjz80zz71z9iz90za3z66zs0z65zz65zq8z75zlaz81zcz66zj6g2mz78zz76zmz66z22z75zfcz69zz66z line-list-type-bullet ace-ltr" dir="auto" id="editor-3-ace-line-42"><ul class="listtype-bullet listindent1 list-bullet1"><li><span class="ace-line-pocket-zws" data-faketext="" data-contentcollector-ignore-space-at="end"></span><span class="ace-line-pocket" data-faketext="" contenteditable="false"></span><span class="ace-line-pocket-zws" data-faketext="" data-contentcollector-ignore-space-at="start"></span><span>Item 1</span></li></ul></div><div class="ace-line gutter-author-d-iz88z86z86za0dz67zz78zz78zz74zz68zjz80zz71z9iz90za3z66zs0z65zz65zq8z75zlaz81zcz66zj6g2mz78zz76zmz66z22z75zfcz69zz66z line-list-type-bullet ace-ltr" dir="auto" id="editor-3-ace-line-43"><ul class="listtype-bullet listindent1 list-bullet1"><li><span class="ace-line-pocket-zws" data-faketext="" data-contentcollector-ignore-space-at="end"></span><span class="ace-line-pocket" data-faketext="" contenteditable="false"></span><span class="ace-line-pocket-zws" data-faketext="" data-contentcollector-ignore-space-at="start"></span><span>Item 2</span></li></ul></div>';
|
||||
assert.equal(
|
||||
copy_and_paste.paste_handler_converter(input),
|
||||
"Test List:\n* Item 1\n* Item 2",
|
||||
"Test list:\n* Item 1\n* Item 2",
|
||||
);
|
||||
|
||||
let data = "<p>text</p>";
|
||||
|
|
|
@ -59,13 +59,13 @@ const draft_1 = {
|
|||
stream: "stream",
|
||||
topic: "topic",
|
||||
type: "stream",
|
||||
content: "Test Stream Message",
|
||||
content: "Test stream message",
|
||||
};
|
||||
const draft_2 = {
|
||||
private_message_recipient: "aaron@zulip.com",
|
||||
reply_to: "aaron@zulip.com",
|
||||
type: "private",
|
||||
content: "Test Private Message",
|
||||
content: "Test private message",
|
||||
};
|
||||
const short_msg = {
|
||||
stream: "stream",
|
||||
|
@ -185,14 +185,14 @@ test("remove_old_drafts", () => {
|
|||
stream: "stream",
|
||||
subject: "topic",
|
||||
type: "stream",
|
||||
content: "Test Stream Message",
|
||||
content: "Test stream message",
|
||||
updatedAt: Date.now(),
|
||||
};
|
||||
const draft_4 = {
|
||||
private_message_recipient: "aaron@zulip.com",
|
||||
reply_to: "aaron@zulip.com",
|
||||
type: "private",
|
||||
content: "Test Private Message",
|
||||
content: "Test private message",
|
||||
updatedAt: new Date().setDate(-30),
|
||||
};
|
||||
const draft_model = drafts.draft_model;
|
||||
|
@ -220,35 +220,35 @@ test("format_drafts", (override) => {
|
|||
stream: "stream",
|
||||
topic: "topic",
|
||||
type: "stream",
|
||||
content: "Test Stream Message",
|
||||
content: "Test stream message",
|
||||
updatedAt: feb12().getTime(),
|
||||
};
|
||||
const draft_2 = {
|
||||
private_message_recipient: "aaron@zulip.com",
|
||||
reply_to: "aaron@zulip.com",
|
||||
type: "private",
|
||||
content: "Test Private Message",
|
||||
content: "Test private message",
|
||||
updatedAt: date(-1),
|
||||
};
|
||||
const draft_3 = {
|
||||
stream: "stream 2",
|
||||
subject: "topic",
|
||||
type: "stream",
|
||||
content: "Test Stream Message 2",
|
||||
content: "Test stream message 2",
|
||||
updatedAt: date(-10),
|
||||
};
|
||||
const draft_4 = {
|
||||
private_message_recipient: "aaron@zulip.com",
|
||||
reply_to: "iago@zulip.com",
|
||||
type: "private",
|
||||
content: "Test Private Message 2",
|
||||
content: "Test private message 2",
|
||||
updatedAt: date(-5),
|
||||
};
|
||||
const draft_5 = {
|
||||
private_message_recipient: "aaron@zulip.com",
|
||||
reply_to: "zoe@zulip.com",
|
||||
type: "private",
|
||||
content: "Test Private Message 3",
|
||||
content: "Test private message 3",
|
||||
updatedAt: date(-2),
|
||||
};
|
||||
|
||||
|
@ -260,28 +260,28 @@ test("format_drafts", (override) => {
|
|||
stream_color: "#FFFFFF",
|
||||
dark_background: "",
|
||||
topic: "topic",
|
||||
raw_content: "Test Stream Message",
|
||||
raw_content: "Test stream message",
|
||||
time_stamp: "7:55 AM",
|
||||
},
|
||||
{
|
||||
draft_id: "id2",
|
||||
is_stream: false,
|
||||
recipients: "aaron@zulip.com",
|
||||
raw_content: "Test Private Message",
|
||||
raw_content: "Test private message",
|
||||
time_stamp: "Jan 30",
|
||||
},
|
||||
{
|
||||
draft_id: "id5",
|
||||
is_stream: false,
|
||||
recipients: "aaron@zulip.com",
|
||||
raw_content: "Test Private Message 3",
|
||||
raw_content: "Test private message 3",
|
||||
time_stamp: "Jan 29",
|
||||
},
|
||||
{
|
||||
draft_id: "id4",
|
||||
is_stream: false,
|
||||
recipients: "aaron@zulip.com",
|
||||
raw_content: "Test Private Message 2",
|
||||
raw_content: "Test private message 2",
|
||||
time_stamp: "Jan 26",
|
||||
},
|
||||
{
|
||||
|
@ -291,7 +291,7 @@ test("format_drafts", (override) => {
|
|||
stream_color: "#FFFFFF",
|
||||
dark_background: "",
|
||||
topic: "topic",
|
||||
raw_content: "Test Stream Message 2",
|
||||
raw_content: "Test stream message 2",
|
||||
time_stamp: "Jan 21",
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1125,7 +1125,7 @@ test("describe", () => {
|
|||
});
|
||||
|
||||
test("can_bucket_by", () => {
|
||||
let terms = [{operator: "stream", operand: "My Stream"}];
|
||||
let terms = [{operator: "stream", operand: "My stream"}];
|
||||
let filter = new Filter(terms);
|
||||
assert.equal(filter.can_bucket_by("stream"), true);
|
||||
assert.equal(filter.can_bucket_by("stream", "topic"), false);
|
||||
|
@ -1133,8 +1133,8 @@ test("can_bucket_by", () => {
|
|||
|
||||
terms = [
|
||||
// try a non-orthodox ordering
|
||||
{operator: "topic", operand: "My Topic"},
|
||||
{operator: "stream", operand: "My Stream"},
|
||||
{operator: "topic", operand: "My topic"},
|
||||
{operator: "stream", operand: "My stream"},
|
||||
];
|
||||
filter = new Filter(terms);
|
||||
assert.equal(filter.can_bucket_by("stream"), true);
|
||||
|
@ -1142,8 +1142,8 @@ test("can_bucket_by", () => {
|
|||
assert.equal(filter.can_bucket_by("pm-with"), false);
|
||||
|
||||
terms = [
|
||||
{operator: "stream", operand: "My Stream", negated: true},
|
||||
{operator: "topic", operand: "My Topic"},
|
||||
{operator: "stream", operand: "My stream", negated: true},
|
||||
{operator: "topic", operand: "My topic"},
|
||||
];
|
||||
filter = new Filter(terms);
|
||||
assert.equal(filter.can_bucket_by("stream"), false);
|
||||
|
|
|
@ -54,8 +54,8 @@ run_test("case insensitivity", () => {
|
|||
assert.deepEqual(Array.from(d.keys()), []);
|
||||
|
||||
assert(!d.has("foo"));
|
||||
d.set("fOO", "Hello World");
|
||||
assert.equal(d.get("foo"), "Hello World");
|
||||
d.set("fOO", "Hello world");
|
||||
assert.equal(d.get("foo"), "Hello world");
|
||||
assert(d.has("foo"));
|
||||
assert(d.has("FOO"));
|
||||
assert(!d.has("not_a_key"));
|
||||
|
|
|
@ -128,7 +128,7 @@ const backend = {
|
|||
const edgecase_group = {
|
||||
name: "Bobby <h1>Tables</h1>",
|
||||
id: 3,
|
||||
description: "HTML Syntax to check for Markdown edge cases.",
|
||||
description: "HTML syntax to check for Markdown edge cases.",
|
||||
members: [],
|
||||
};
|
||||
|
||||
|
|
|
@ -226,8 +226,8 @@ run_test("merge_message_groups", () => {
|
|||
id: _.uniqueId("test_message_"),
|
||||
status_message: false,
|
||||
type: "stream",
|
||||
stream: "Test Stream 1",
|
||||
topic: "Test Subject 1",
|
||||
stream: "Test stream 1",
|
||||
topic: "Test subject 1",
|
||||
sender_email: "test@example.com",
|
||||
timestamp: (next_timestamp += 1),
|
||||
...message,
|
||||
|
@ -431,7 +431,7 @@ run_test("merge_message_groups", () => {
|
|||
const message1 = build_message_context();
|
||||
const message_group1 = build_message_group([message1]);
|
||||
|
||||
const message2 = build_message_context({topic: "Test Subject 2"});
|
||||
const message2 = build_message_context({topic: "Test subject 2"});
|
||||
const message_group2 = build_message_group([message2]);
|
||||
|
||||
const list = build_list([message_group1]);
|
||||
|
@ -449,7 +449,7 @@ run_test("merge_message_groups", () => {
|
|||
const message1 = build_message_context({timestamp: 900000});
|
||||
const message_group1 = build_message_group([message1]);
|
||||
|
||||
const message2 = build_message_context({topic: "Test Subject 2", timestamp: 1000});
|
||||
const message2 = build_message_context({topic: "Test subject 2", timestamp: 1000});
|
||||
const message_group2 = build_message_group([message2]);
|
||||
|
||||
const list = build_list([message_group1]);
|
||||
|
|
|
@ -49,7 +49,7 @@ run_test("get_unread_ids", () => {
|
|||
let terms;
|
||||
|
||||
const sub = {
|
||||
name: "My Stream",
|
||||
name: "My stream",
|
||||
stream_id: 55,
|
||||
};
|
||||
|
||||
|
|
|
@ -284,8 +284,8 @@ test("sending", (override) => {
|
|||
|
||||
// similarly, we only exercise the failure codepath
|
||||
// Since this path calls blueslip.warn, we need to handle it.
|
||||
blueslip.expect("warn", "XHR Error Message.");
|
||||
channel.xhr_error_message = () => "XHR Error Message.";
|
||||
blueslip.expect("warn", "XHR error message.");
|
||||
channel.xhr_error_message = () => "XHR error message.";
|
||||
args.error();
|
||||
}
|
||||
emoji_name = "alien"; // not set yet
|
||||
|
|
|
@ -25,7 +25,7 @@ const stream4 = 4;
|
|||
const stream5 = 5; // Deleted stream
|
||||
|
||||
// Topics in the stream, all unread except topic1 & stream1.
|
||||
const topic1 = "topic-1"; // No Other sender & read.
|
||||
const topic1 = "topic-1"; // No other sender & read.
|
||||
const topic2 = "topic-2"; // Other sender
|
||||
const topic3 = "topic-3"; // User not present
|
||||
const topic4 = "topic-4"; // User not present
|
||||
|
@ -786,31 +786,31 @@ test("test_topic_edit", (override) => {
|
|||
|
||||
test("test_search", () => {
|
||||
rt.clear_for_tests();
|
||||
assert.equal(rt.topic_in_search_results("t", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("T", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("to", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("top", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("ToP", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("Topi", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("tOpi", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("toPic", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("Topic", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("topic", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("recent", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("RECENT", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("t", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("T", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("to", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("top", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("ToP", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("Topi", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("tOpi", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("toPic", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("Topic", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("topic", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("recent", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("RECENT", "general", "Recent topic"), true);
|
||||
|
||||
// match in any order of words
|
||||
assert.equal(rt.topic_in_search_results("topic recent", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("topic recent", "general", "Recent topic"), true);
|
||||
|
||||
// Matches any sequence of words.
|
||||
assert.equal(rt.topic_in_search_results("o", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("nt to", "general", "Recent Topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("z", "general", "Recent Topic"), false);
|
||||
assert.equal(rt.topic_in_search_results("o", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("nt to", "general", "Recent topic"), true);
|
||||
assert.equal(rt.topic_in_search_results("z", "general", "Recent topic"), false);
|
||||
|
||||
assert.equal(rt.topic_in_search_results("?", "general", "Recent Topic"), false);
|
||||
assert.equal(rt.topic_in_search_results("?", "general", "Recent topic"), false);
|
||||
|
||||
// Test special character match
|
||||
assert.equal(rt.topic_in_search_results(".*+?^${}()[]\\", "general", "Recent Topic"), false);
|
||||
assert.equal(rt.topic_in_search_results(".*+?^${}()[]\\", "general", "Recent topic"), false);
|
||||
assert.equal(rt.topic_in_search_results("?", "general", "not-at-start?"), true);
|
||||
|
||||
assert.equal(rt.topic_in_search_results("?", "general", "?"), true);
|
||||
|
|
|
@ -254,7 +254,7 @@ run_test("spoiler-header", () => {
|
|||
$content.set_find_results("div.spoiler-header", $array([$header]));
|
||||
|
||||
// Test that the show/hide button gets added to a spoiler header.
|
||||
const label = "My Spoiler Header";
|
||||
const label = "My spoiler header";
|
||||
const toggle_button_html =
|
||||
'<span class="spoiler-button" aria-expanded="false"><span class="spoiler-arrow"></span></span>';
|
||||
$header.html(label);
|
||||
|
|
|
@ -16,7 +16,7 @@ const SHORT_TEXT_ID = 1;
|
|||
const SELECT_ID = 3;
|
||||
const EXTERNAL_ACCOUNT_ID = 7;
|
||||
|
||||
const SHORT_TEXT_NAME = "Short Text";
|
||||
const SHORT_TEXT_NAME = "Short text";
|
||||
const SELECT_NAME = "Select";
|
||||
const EXTERNAL_ACCOUNT_NAME = "External account";
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ test("sort_streams", (override) => {
|
|||
{
|
||||
stream_id: 304,
|
||||
name: "Derp",
|
||||
description: "Always Derping",
|
||||
description: "Always derping",
|
||||
subscribed: false,
|
||||
},
|
||||
{
|
||||
|
@ -344,24 +344,24 @@ test("sort_recipients", () => {
|
|||
recent_senders.process_message_for_senders({
|
||||
sender_id: 7,
|
||||
stream_id: 1,
|
||||
topic: "Dev Topic",
|
||||
topic: "Dev topic",
|
||||
id: (next_id += 1),
|
||||
});
|
||||
recent_senders.process_message_for_senders({
|
||||
sender_id: 5,
|
||||
stream_id: 1,
|
||||
topic: "Dev Topic",
|
||||
topic: "Dev topic",
|
||||
id: (next_id += 1),
|
||||
});
|
||||
recent_senders.process_message_for_senders({
|
||||
sender_id: 6,
|
||||
stream_id: 1,
|
||||
topic: "Dev Topic",
|
||||
topic: "Dev topic",
|
||||
id: (next_id += 1),
|
||||
});
|
||||
|
||||
// Typeahead for stream message [query, stream-name, topic-name]
|
||||
assert.deepEqual(get_typeahead_result("b", "Dev", "Dev Topic"), [
|
||||
assert.deepEqual(get_typeahead_result("b", "Dev", "Dev topic"), [
|
||||
subscriber_email_3,
|
||||
subscriber_email_2,
|
||||
subscriber_email_1,
|
||||
|
@ -374,18 +374,18 @@ test("sort_recipients", () => {
|
|||
recent_senders.process_message_for_senders({
|
||||
sender_id: 5,
|
||||
stream_id: 2,
|
||||
topic: "Linux Topic",
|
||||
topic: "Linux topic",
|
||||
id: (next_id += 1),
|
||||
});
|
||||
recent_senders.process_message_for_senders({
|
||||
sender_id: 7,
|
||||
stream_id: 2,
|
||||
topic: "Linux Topic",
|
||||
topic: "Linux topic",
|
||||
id: (next_id += 1),
|
||||
});
|
||||
|
||||
// No match
|
||||
assert.deepEqual(get_typeahead_result("h", "Linux", "Linux Topic"), [
|
||||
assert.deepEqual(get_typeahead_result("h", "Linux", "Linux topic"), [
|
||||
"zman@test.net",
|
||||
"b_user_3@zulip.net",
|
||||
"a_user@zulip.org",
|
||||
|
@ -405,7 +405,7 @@ test("sort_recipients all mention", () => {
|
|||
// Test person email is "all" or "everyone"
|
||||
const test_objs = matches.concat([all_obj]);
|
||||
|
||||
const results = th.sort_recipients(test_objs, "a", "Linux", "Linux Topic");
|
||||
const results = th.sort_recipients(test_objs, "a", "Linux", "Linux topic");
|
||||
|
||||
assertSameEmails(results, [all_obj, a_bot, a_user, b_user_1, b_user_2, b_user_3, b_bot, zman]);
|
||||
});
|
||||
|
@ -433,7 +433,7 @@ test("sort_recipients pm counts", () => {
|
|||
const linux_sub = stream_data.get_sub("Linux");
|
||||
peer_data.add_subscriber(linux_sub.stream_id, b_user_3.user_id);
|
||||
|
||||
assert.deepEqual(get_typeahead_result("b", "Linux", "Linux Topic"), [
|
||||
assert.deepEqual(get_typeahead_result("b", "Linux", "Linux topic"), [
|
||||
"b_user_3@zulip.net",
|
||||
"b_user_1@zulip.net",
|
||||
"b_user_2@zulip.net",
|
||||
|
@ -482,7 +482,7 @@ test("sort_recipients dup alls", () => {
|
|||
// full_name starts with same character but emails are 'all'
|
||||
const test_objs = [all_obj, a_user, all_obj];
|
||||
|
||||
const recipients = th.sort_recipients(test_objs, "a", "Linux", "Linux Topic");
|
||||
const recipients = th.sort_recipients(test_objs, "a", "Linux", "Linux topic");
|
||||
|
||||
const expected = [all_obj, all_obj, a_user];
|
||||
assertSameEmails(recipients, expected);
|
||||
|
@ -491,7 +491,7 @@ test("sort_recipients dup alls", () => {
|
|||
test("sort_recipients subscribers", () => {
|
||||
// b_user_2 is a subscriber and b_user_1 is not.
|
||||
const small_matches = [b_user_2, b_user_1];
|
||||
const recipients = th.sort_recipients(small_matches, "b", "Dev", "Dev Topic");
|
||||
const recipients = th.sort_recipients(small_matches, "b", "Dev", "Dev topic");
|
||||
const recipients_email = recipients.map((person) => person.email);
|
||||
const expected = ["b_user_2@zulip.net", "b_user_1@zulip.net"];
|
||||
assert.deepEqual(recipients_email, expected);
|
||||
|
@ -501,7 +501,7 @@ test("sort_recipients pm partners", () => {
|
|||
// b_user_3 is a pm partner and b_user_2 is not and
|
||||
// both are not subscribered to the stream Linux.
|
||||
const small_matches = [b_user_3, b_user_2];
|
||||
const recipients = th.sort_recipients(small_matches, "b", "Linux", "Linux Topic");
|
||||
const recipients = th.sort_recipients(small_matches, "b", "Linux", "Linux topic");
|
||||
const recipients_email = recipients.map((person) => person.email);
|
||||
const expected = ["b_user_3@zulip.net", "b_user_2@zulip.net"];
|
||||
assert.deepEqual(recipients_email, expected);
|
||||
|
@ -629,7 +629,7 @@ test("render_stream", () => {
|
|||
const stream = {
|
||||
description: "This is a short description.",
|
||||
stream_id: 42,
|
||||
name: "Short Description",
|
||||
name: "Short description",
|
||||
};
|
||||
|
||||
stub_templates((template_name, args) => {
|
||||
|
@ -649,7 +649,7 @@ test("render_stream w/long description", () => {
|
|||
const stream = {
|
||||
description: "This is a very very very very very long description.",
|
||||
stream_id: 43,
|
||||
name: "Long Description",
|
||||
name: "Long description",
|
||||
};
|
||||
|
||||
stub_templates((template_name, args) => {
|
||||
|
|
|
@ -249,7 +249,7 @@ test("num_unread_for_topic", (override) => {
|
|||
|
||||
override(sub_store, "get", (arg) => {
|
||||
if (arg === stream_id) {
|
||||
return {name: "Some Stream"};
|
||||
return {name: "Some stream"};
|
||||
}
|
||||
throw new Error(`Unknown stream ${arg}`);
|
||||
});
|
||||
|
|
|
@ -614,7 +614,7 @@ test("uppy_events", (override) => {
|
|||
|
||||
state = {
|
||||
type: "error",
|
||||
details: "Some Error",
|
||||
details: "Some error",
|
||||
message: "Some error message",
|
||||
};
|
||||
const on_info_visible_callback = callbacks["info-visible"];
|
||||
|
|
|
@ -62,8 +62,8 @@ run_test("basics", () => {
|
|||
widget.html("<b>hello</b>");
|
||||
assert.equal(widget.html(), "<b>hello</b>");
|
||||
|
||||
widget.prop("title", "My Widget");
|
||||
assert.equal(widget.prop("title"), "My Widget");
|
||||
widget.prop("title", "My widget");
|
||||
assert.equal(widget.prop("title"), "My widget");
|
||||
|
||||
widget.val("42");
|
||||
assert.equal(widget.val(), "42");
|
||||
|
|
|
@ -37,7 +37,7 @@ async function test_empty_drafts(page: Page): Promise<void> {
|
|||
}
|
||||
|
||||
async function create_stream_message_draft(page: Page): Promise<void> {
|
||||
console.log("Creating Stream Message Draft");
|
||||
console.log("Creating stream message draft");
|
||||
await page.keyboard.press("KeyC");
|
||||
await page.waitForSelector("#stream-message", {visible: true});
|
||||
await common.fill_form(page, "form#send_message_form", {
|
||||
|
@ -116,7 +116,7 @@ async function test_previously_created_drafts_rendered(page: Page): Promise<void
|
|||
}
|
||||
|
||||
async function test_restore_message_draft(page: Page): Promise<void> {
|
||||
console.log("Restoring Stream Message Draft");
|
||||
console.log("Restoring stream message draft");
|
||||
await page.click("#drafts_table .message_row:not(.private-message) .restore-draft");
|
||||
await wait_for_drafts_to_dissapear(page);
|
||||
await page.waitForSelector("#stream-message", {visible: true});
|
||||
|
@ -137,7 +137,7 @@ async function edit_stream_message_draft(page: Page): Promise<void> {
|
|||
await common.fill_form(page, "form#send_message_form", {
|
||||
stream_message_recipient_stream: "all",
|
||||
stream_message_recipient_topic: "tests",
|
||||
content: "Updated Stream Message",
|
||||
content: "Updated stream message",
|
||||
});
|
||||
await page.click("#compose_close");
|
||||
}
|
||||
|
@ -166,7 +166,7 @@ async function test_edited_draft_message(page: Page): Promise<void> {
|
|||
page,
|
||||
".draft-row:nth-last-child(2) .rendered_markdown.restore-draft",
|
||||
),
|
||||
"Updated Stream Message",
|
||||
"Updated stream message",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ async function test_get_api_key(page: Page): Promise<void> {
|
|||
});
|
||||
|
||||
// When typing the password in Firefox, it shows "Not Secure" warning
|
||||
// which was hiding the Get API Key button.
|
||||
// which was hiding the Get API key button.
|
||||
// You can see the screenshot of it in https://github.com/zulip/zulip/pull/17136.
|
||||
// Focusing on it will remove the warning.
|
||||
await page.focus(get_api_key_button_selector);
|
||||
|
|
|
@ -8,7 +8,7 @@ class zulip_ops::profile::base {
|
|||
'mosh',
|
||||
# package management
|
||||
'aptitude',
|
||||
# SSL Certificates
|
||||
# SSL certificates
|
||||
'certbot',
|
||||
# Security
|
||||
'iptables-persistent',
|
||||
|
|
|
@ -456,11 +456,11 @@ export function initialize(home_view_loaded) {
|
|||
// more performant (i.e. avoids this unnecessary extra fetch the
|
||||
// results of which are basically discarded) and better represents
|
||||
// more than a few hundred messages' history, but this strategy
|
||||
// allows "Recent Topics" to always show current data (with gaps)
|
||||
// allows "Recent topics" to always show current data (with gaps)
|
||||
// on page load; the data will be complete once the algorithm
|
||||
// above catches up to present.
|
||||
//
|
||||
// (Users will see a weird artifact where Recent Topics has a gap
|
||||
// (Users will see a weird artifact where Recent topics has a gap
|
||||
// between E.g. 6 days ago and 37 days ago while the catchup
|
||||
// process runs, so this strategy still results in problematic
|
||||
// visual artifacts shortly after page load; just more forgiveable
|
||||
|
|
|
@ -828,7 +828,7 @@ export function deactivate(coming_from_recent_topics = false) {
|
|||
message_lists.home in it.
|
||||
*/
|
||||
search.clear_search_form();
|
||||
// Both All messages and Recent Topics have `undefined` filter.
|
||||
// Both All messages and Recent topics have `undefined` filter.
|
||||
// Return if already in the All message narrow.
|
||||
if (narrow_state.filter() === undefined && !coming_from_recent_topics) {
|
||||
return;
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as channel from "../channel";
|
|||
// Main JavaScript file for the integrations development panel at
|
||||
// /devtools/integrations.
|
||||
|
||||
// Data Segment: We lazy load the requested fixtures from the backend
|
||||
// Data segment: We lazy load the requested fixtures from the backend
|
||||
// as and when required and then cache them here.
|
||||
|
||||
const loaded_fixtures = new Map();
|
||||
|
@ -106,9 +106,9 @@ function set_results(response) {
|
|||
for (const response of responses) {
|
||||
if (response.fixture_name !== undefined) {
|
||||
data += "Fixture: " + response.fixture_name;
|
||||
data += "\nStatus Code: " + response.status_code;
|
||||
data += "\nStatus code: " + response.status_code;
|
||||
} else {
|
||||
data += "Status Code: " + response.status_code;
|
||||
data += "Status code: " + response.status_code;
|
||||
}
|
||||
data += "\nResponse: " + response.message + "\n\n";
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ function update_url() {
|
|||
return;
|
||||
}
|
||||
|
||||
// API Callers: These methods handle communicating with the Python backend API.
|
||||
// API callers: These methods handle communicating with the Python backend API.
|
||||
function handle_unsuccessful_response(response) {
|
||||
try {
|
||||
const status_code = response.statusCode().status;
|
||||
|
@ -337,7 +337,7 @@ $(() => {
|
|||
]);
|
||||
|
||||
$("#stream_name")[0].value = "Denmark";
|
||||
$("#topic_name")[0].value = "Integrations Testing";
|
||||
$("#topic_name")[0].value = "Integrations testing";
|
||||
|
||||
const potential_default_bot = $("#bot_name")[0][1];
|
||||
if (potential_default_bot !== undefined) {
|
||||
|
|
|
@ -114,7 +114,7 @@ function create_choice_row(container) {
|
|||
function clear_form_data() {
|
||||
$("#profile_field_name").val("").closest(".control-group").show();
|
||||
$("#profile_field_hint").val("").closest(".control-group").show();
|
||||
// Set default type "Short Text" in field type dropdown
|
||||
// Set default type "Short text" in field type dropdown
|
||||
$("#profile_field_type").val(field_types.SHORT_TEXT.id);
|
||||
// Clear data from select field form
|
||||
$("#profile_field_choices").html("");
|
||||
|
|
|
@ -9,7 +9,7 @@ import _ from "lodash";
|
|||
|
||||
// See backend fenced_code.py:71 for associated regexp
|
||||
const fencestr =
|
||||
"^(~{3,}|`{3,})" + // Opening Fence
|
||||
"^(~{3,}|`{3,})" + // Opening fence
|
||||
"[ ]*" + // Spaces
|
||||
"(" +
|
||||
"\\{?\\.?" +
|
||||
|
|
|
@ -256,7 +256,7 @@
|
|||
|
||||
.keyboard_tip::before {
|
||||
display: inline;
|
||||
content: "\f11c Keyboard Shortcut: ";
|
||||
content: "\f11c Keyboard shortcut: ";
|
||||
font-family: FontAwesome, "Source Sans 3", sans-serif;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<p>
|
||||
{{#tr}}
|
||||
For example, to configure code playgrounds for languages like Python or JavaScript, you could specify the <i>Language</i>
|
||||
and <i>URL Prefix</i> fields as:
|
||||
and <i>URL prefix</i> fields as:
|
||||
{{/tr}}
|
||||
</p>
|
||||
<ul>
|
||||
|
|
|
@ -219,7 +219,7 @@ These configuration options are declared as follows:
|
|||
|
||||
```
|
||||
WebhookIntegration('helloworld', ['misc'], display_name='Hello World',
|
||||
config_options=[('HelloWorld API Key', 'hw_api_key', check_string)])
|
||||
config_options=[('HelloWorld API key', 'hw_api_key', check_string)])
|
||||
```
|
||||
|
||||
`config_options` is a list describing the parameters the user should
|
||||
|
@ -251,7 +251,7 @@ After running the above command, you should see something similar to:
|
|||
{"msg":"","result":"success"}
|
||||
```
|
||||
|
||||
### Management Command: send_webhook_fixture_message
|
||||
### Management command: send_webhook_fixture_message
|
||||
|
||||
Using `manage.py` from within the Zulip development environment:
|
||||
|
||||
|
@ -471,7 +471,7 @@ request:
|
|||
https://zulip.readthedocs.io/en/latest/contributing/code-style.html) and take a look
|
||||
through your code to double-check that you've followed Zulip's guidelines.
|
||||
3. Take a look at your Git history to ensure your commits have been clear and
|
||||
logical (see [Version Control](
|
||||
logical (see [Version control](
|
||||
https://zulip.readthedocs.io/en/latest/contributing/version-control.html) for tips). If not,
|
||||
consider revising them with `git rebase --interactive`. For most incoming webhooks,
|
||||
you'll want to squash your changes into a single commit and include a good,
|
||||
|
@ -519,7 +519,7 @@ def test_unknown_action_no_data(self) -> None:
|
|||
result = self.client_post(self.url, 'unknown_action', **post_params)
|
||||
|
||||
# check that we got the expected error message
|
||||
self.assert_json_error(result, "Unknown WordPress webhook action: WordPress Action")
|
||||
self.assert_json_error(result, "Unknown WordPress webhook action: WordPress action")
|
||||
```
|
||||
|
||||
In a normal test, `check_webhook` would handle all the setup
|
||||
|
@ -572,17 +572,17 @@ attribute `TOPIC` as a keyword argument to `build_webhook_url`, like so:
|
|||
class QuerytestHookTests(WebhookTestCase):
|
||||
|
||||
STREAM_NAME = 'querytest'
|
||||
TOPIC = "Default Topic"
|
||||
TOPIC = "Default topic"
|
||||
URL_TEMPLATE = "/api/v1/external/querytest?api_key={api_key}&stream={stream}"
|
||||
FIXTURE_DIR_NAME = 'querytest'
|
||||
|
||||
def test_querytest_test_one(self) -> None:
|
||||
# construct the URL used for this test
|
||||
self.TOPIC = "Query Test"
|
||||
self.TOPIC = "Query test"
|
||||
self.url = self.build_webhook_url(topic=self.TOPIC)
|
||||
|
||||
# define the expected message contents
|
||||
expected_topic = "Query Test"
|
||||
expected_topic = "Query test"
|
||||
expected_message = "This is a test of custom query parameters."
|
||||
|
||||
self.check_webhook('test_one', expected_topic, expected_message,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Error Handling
|
||||
# Error handling
|
||||
|
||||
Zulip's API will always return a JSON format response.
|
||||
The HTTP status code indicates whether the request was successful
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
* [API keys](/api/api-keys)
|
||||
* [Configuring the Python bindings](/api/configuring-python-bindings)
|
||||
* [Error handling](/api/rest-error-handling)
|
||||
* [Client Libraries](/api/client-libraries)
|
||||
* [Client libraries](/api/client-libraries)
|
||||
* [Changelog](/api/changelog)
|
||||
|
||||
{!rest-endpoints.md!}
|
||||
|
|
|
@ -25,7 +25,7 @@ More examples and documentation can be found [here](https://github.com/zulip/zul
|
|||
|
||||
{generate_api_arguments_table|zulip.yaml|/messages/flags:post}
|
||||
|
||||
## Available Flags
|
||||
## Available flags
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
|
|
|
@ -126,9 +126,9 @@ Response: 12.0 meter = 13.12336 yard
|
|||
|
||||
> zulip-terminal -b ~/followup.conf followup
|
||||
|
||||
Enter your message: "Task Completed"
|
||||
Enter your message: "Task completed"
|
||||
Response: stream: followup topic: foo_sender@zulip.com
|
||||
from foo_sender@zulip.com: Task Completed
|
||||
from foo_sender@zulip.com: Task completed
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
<ul class="dropdown-menu" role="menu" aria-labelledby="settings-dropdown">
|
||||
{#
|
||||
It is quite ingrained in our frontend code that your Home
|
||||
view is a Bootstrap Nav tab, even though we don't show the tab
|
||||
view is a Bootstrap nav tab, even though we don't show the tab
|
||||
anymore
|
||||
#}
|
||||
<li class="invisible" style="display:none;" role="presentation"><a href="#message_feed_container" data-toggle="tab"></a></li>
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
<td>Free and open source</td>
|
||||
<td class="yes"></td>
|
||||
<td class="no"></td>
|
||||
<td>Open Core *</td>
|
||||
<td>Open core *</td>
|
||||
<td class="no"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
@ -70,14 +70,14 @@
|
|||
<br />
|
||||
|
||||
<div class="row2">
|
||||
<label for="URL"><b>URL</b> (Automatically Generated)</label>
|
||||
<label for="URL"><b>URL</b> (automatically generated)</label>
|
||||
<input id="URL" type="text" />
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="row3">
|
||||
<label for="custom_http_headers"><b>Custom HTTP Headers</b></label>
|
||||
<label for="custom_http_headers"><b>Custom HTTP headers</b></label>
|
||||
<textarea id="custom_http_headers"></textarea>
|
||||
</div>
|
||||
|
||||
|
@ -86,12 +86,12 @@
|
|||
<div class="row4">
|
||||
<div class="col1">
|
||||
<div class="inline">
|
||||
<label for="fixture_body"><b>Fixture Body</b></label>
|
||||
<label for="fixture_body"><b>Fixture body</b></label>
|
||||
<span id="results_notice"></span>
|
||||
</div>
|
||||
<textarea id="fixture_body"></textarea>
|
||||
<div class="buttons">
|
||||
<button id="send_all_fixtures_button">Send All</button>
|
||||
<button id="send_all_fixtures_button">Send all</button>
|
||||
<button id="send_fixture_button">Send!</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<div class="digest-container">
|
||||
<div class="digest-email-container">
|
||||
<div class="portico-page" id="digest-page-title">
|
||||
<h1> Zulip Digest </h1>
|
||||
<h1> Zulip digest </h1>
|
||||
</div>
|
||||
<div class="digest-email-html">
|
||||
{% include 'zerver/emails/compiled/digest.html' %}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{% endif %}
|
||||
<h4>From: {{ from_email }}</h4>
|
||||
{% if reply_to %}
|
||||
<h4>Reply To:
|
||||
<h4>Reply to:
|
||||
{% for email in reply_to %}
|
||||
{{ email }}
|
||||
{% endfor %}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "zerver/portico.html" %}
|
||||
{% set entrypoint = "landing-page" %}
|
||||
|
||||
{% set OPEN_GRAPH_TITLE = 'Zulip Features' %}
|
||||
{% set OPEN_GRAPH_TITLE = 'Zulip features' %}
|
||||
{% set OPEN_GRAPH_DESCRIPTION = 'First class threading on top of everything you could want from real-time chat.' %}
|
||||
|
||||
{% block portico_content %}
|
||||
|
|
|
@ -53,7 +53,7 @@ Zulip teams are still snappy at thousands of users.
|
|||
|
||||
### Hundreds of integrations.
|
||||
|
||||
Get events from GitHub, Stripe, Travis CI, JIRA, and
|
||||
Get events from GitHub, Stripe, Travis CI, Jira, and
|
||||
[hundreds of other tools](/integrations) right in Zulip. Use topics to give
|
||||
each issue or decision its own place for discussion. Link to tickets in
|
||||
external sites with
|
||||
|
|
|
@ -157,7 +157,7 @@ your organization.
|
|||
|
||||
### Hundreds of integrations.
|
||||
|
||||
Get events from GitHub, Travis CI, JIRA, and
|
||||
Get events from GitHub, Travis CI, Jira, and
|
||||
[hundreds of other tools](/integrations) right in Zulip. Topics give each
|
||||
issue its own place for discussion.
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ describes academic research better than any of our other use cases.
|
|||
Zulip has long been popular with individual research groups, but
|
||||
during the pandemic has started being used for large distributed
|
||||
communities focused around research topics like category theory or the
|
||||
Lean Theorem Prover. We enthusiastically provide free hosting for
|
||||
Lean theorem prover. We enthusiastically provide free hosting for
|
||||
both use cases.
|
||||
|
||||
If you haven’t read [why Zulip](/why-zulip), read that first. The
|
||||
|
@ -162,12 +162,12 @@ hosting to your own servers.
|
|||
|
||||
### Free and open source.
|
||||
|
||||
Unlike many modern "open source" applications that are actually Open
|
||||
Core, Zulip is 100% Free and Open Source software. All code,
|
||||
Unlike many modern "open source" applications that are actually open
|
||||
core, Zulip is 100% free and open source software. All code,
|
||||
including for the [server](https://github.com/zulip/zulip),
|
||||
[desktop](https://github.com/zulip/zulip-desktop),
|
||||
[mobile](https://github.com/zulip/zulip-mobile), and beta
|
||||
[terminal](https://github.com/zulip/zulip-terminal) apps is available
|
||||
[terminal](https://github.com/zulip/zulip-terminal) apps, is available
|
||||
under the Apache 2 license.
|
||||
|
||||
### Created by former academics
|
||||
|
|
|
@ -3,7 +3,7 @@ properly configured. To configure, please check the following:
|
|||
|
||||
* You have added `{{ root_domain_uri }}/complete/gitlab/` as the callback
|
||||
URL in the OAuth application in GitLab. You can register OAuth apps at
|
||||
[GitLab Applications](https://gitlab.com/profile/applications).
|
||||
[GitLab applications](https://gitlab.com/profile/applications).
|
||||
|
||||
* You have set `{{ client_id_key_name }}` in `{{ settings_path }}` and
|
||||
`social_auth_gitlab_secret` in `{{ secrets_path }}` with the values
|
||||
|
|
|
@ -425,8 +425,8 @@
|
|||
</a>
|
||||
<a href="/integrations/doc/jira">
|
||||
<div class="group">
|
||||
<img class="integration-logo" src="/static/images/integrations/logos/jira.svg" alt="{{ _('JIRA logo') }}" />
|
||||
<h3 class="integration-name">JIRA</h3>
|
||||
<img class="integration-logo" src="/static/images/integrations/logos/jira.svg" alt="{{ _('Jira logo') }}" />
|
||||
<h3 class="integration-name">Jira</h3>
|
||||
<p class="integration-description">Monitor project bugs and issues</p>
|
||||
</div>
|
||||
</a>
|
||||
|
|
|
@ -14,7 +14,7 @@ configure a different view as the default.
|
|||
|
||||
{settings_tab|display-settings}
|
||||
|
||||
2. Under **Display Settings**, click on the **Default view** dropdown.
|
||||
2. Under **Display settings**, click on the **Default view** dropdown.
|
||||
|
||||
3. Select a view.
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ If your entire organization speaks a language other than English, an administrat
|
|||
|
||||
{settings_tab|display-settings}
|
||||
|
||||
2. Under **Language Settings**, click the button next to **Default language**.
|
||||
2. Under **Language settings**, click the button next to **Default language**.
|
||||
|
||||
3. Click on a language.
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ prefix**.
|
|||
{end_tabs}
|
||||
|
||||
For example, to configure code playgrounds for languages like Python or
|
||||
JavaScript, you could specify the Language and URL Prefix fields as:
|
||||
JavaScript, you could specify the language and URL prefix fields as:
|
||||
|
||||
* `Python` and `https://replit.com/languages/python3/?code=`
|
||||
* `JavaScript` and `https://replit.com/languages/javascript/?code=`
|
||||
|
@ -105,7 +105,7 @@ in the speficied code playground.
|
|||
* You can configure multiple playgrounds for a given language; if you do that,
|
||||
the user will get to choose which playground to open the code in.
|
||||
|
||||
* The `Language` field is the human-readable Pygments language name for that
|
||||
* The **Language** field is the human-readable Pygments language name for that
|
||||
programming language. The language tag for a code block is internally mapped
|
||||
to these human-readable Pygments names. E.g: `py3` and `py` are mapped to
|
||||
`Python`. One can use the typeahead (which appears when you type something
|
||||
|
|
|
@ -51,7 +51,7 @@ to future versions. <!-- TODO fact check -->
|
|||
|
||||
{tab|linux}
|
||||
|
||||
#### apt (Ubuntu or Debian 8+)
|
||||
#### APT (Ubuntu or Debian 8+)
|
||||
|
||||
1. Enter the following commands into a terminal:
|
||||
|
||||
|
@ -62,7 +62,7 @@ to future versions. <!-- TODO fact check -->
|
|||
sudo apt update
|
||||
sudo apt install zulip
|
||||
|
||||
These commands set up the Zulip Desktop apt repository and its signing
|
||||
These commands set up the Zulip Desktop APT repository and its signing
|
||||
key, and then install the Zulip client.
|
||||
|
||||
1. Run Zulip from your app launcher, or with `zulip` from a terminal.
|
||||
|
|
|
@ -156,7 +156,7 @@ the user interacts with it.
|
|||
~~~
|
||||
Normal content in message
|
||||
|
||||
```spoiler Spoiler Header
|
||||
```spoiler Spoiler header
|
||||
Spoiler content. These lines won't be visible until the user expands the spoiler.
|
||||
```
|
||||
~~~
|
||||
|
@ -197,7 +197,7 @@ the variant that only contains the user ID).
|
|||
|
||||
![Markdown mentions](/static/images/help/markdown-mentions.png)
|
||||
|
||||
## Status Messages
|
||||
## Status messages
|
||||
|
||||
```
|
||||
/me is away
|
||||
|
|
|
@ -9,24 +9,24 @@ There are several possible roles in a Zulip organization.
|
|||
and organization settings. Cannot create or demote organization
|
||||
owners.
|
||||
|
||||
* **Moderator**: Have the permissions of Full Members; additionally,
|
||||
many "Organization permissions" settings allow Moderators to be
|
||||
* **Moderator**: Have the permissions of full members; additionally,
|
||||
many "Organization permissions" settings allow moderators to be
|
||||
given additional privileges or do so by default.
|
||||
|
||||
* **Member**: Has access to all public streams. Member is the default
|
||||
role for most users. [Some organization
|
||||
settings](/help/restrict-permissions-of-new-members) allow an
|
||||
organization to restrict the permissions of **New Members**; Members
|
||||
who do not have those restrictions are called **Full Members**.
|
||||
organization to restrict the permissions of **new members**; Members
|
||||
who do not have those restrictions are called **full members**.
|
||||
|
||||
* **Guest**: Can only view or access streams they've been added to.
|
||||
Guest users interact with public streams as though they were private
|
||||
streams with shared history. Cannot create new streams or invite
|
||||
other users.
|
||||
|
||||
* **Billing Administrator**: The user who upgrades the organization to
|
||||
a paid plan is, in addition to their normal role, a Billing
|
||||
Administrator. Can manage billing in addition to the existing
|
||||
* **Billing administrator**: The user who upgrades the organization to
|
||||
a paid plan is, in addition to their normal role, a billing
|
||||
administrator. Can manage billing in addition to the existing
|
||||
privileges. This allows someone from the billing department to
|
||||
manage billing without needing organization administrator
|
||||
permissions.
|
||||
|
|
|
@ -4,7 +4,7 @@ Zulip supports using SAML authentication for single sign-on, both when
|
|||
self-hosting or on the Zulip Cloud Plus plan.
|
||||
|
||||
This page documents details on how to set up SAML authentication with
|
||||
Zulip with various common SAML Identity Providers.
|
||||
Zulip with various common SAML identity providers.
|
||||
|
||||
## Configure SAML with Okta
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
*If you are running JIRA version 5.2 or greater, or using the hosted
|
||||
JIRA provided by Atlassian, we recommend using the
|
||||
*If you are running Jira version 5.2 or greater, or using the hosted
|
||||
Jira provided by Atlassian, we recommend using the
|
||||
[web-hook method](./jira) above instead. This plugin supports older
|
||||
versions of JIRA.*
|
||||
versions of Jira.*
|
||||
|
||||
{!create-stream.md!}
|
||||
|
||||
|
@ -11,9 +11,9 @@ versions of JIRA.*
|
|||
|
||||
#### Plugin installation
|
||||
|
||||
The JIRA integration plugin requires two JIRA plugins. Please install
|
||||
The Jira integration plugin requires two Jira plugins. Please install
|
||||
the following plugins using the **Universal Plugin Manager** in your
|
||||
JIRA installation:
|
||||
Jira installation:
|
||||
|
||||
* [Script Runner Plugin][script-runner]
|
||||
* [SSL Plugin][ssl-plugin]
|
||||
|
@ -24,21 +24,21 @@ JIRA installation:
|
|||
#### SSL setup
|
||||
|
||||
As Zulip is using a StartCOM SSL certificate that is not recognized by
|
||||
default in the Java installation shipped with JIRA, you will need to
|
||||
tell JIRA about the certificate.
|
||||
default in the Java installation shipped with Jira, you will need to
|
||||
tell Jira about the certificate.
|
||||
|
||||
1. Navigate to **Administration > System > Configure SSL** and in the
|
||||
**Import SSL Certificates** field, enter `{{ api_url }}`.
|
||||
|
||||
2. After clicking **Save Certificates**, follow the on-screen
|
||||
instructions and restart JIRA for it to recognize the proper
|
||||
instructions and restart Jira for it to recognize the proper
|
||||
certificates.
|
||||
|
||||
#### Zulip integration
|
||||
|
||||
1. Copy the folder `integrations/jira/org/` (from the tarball you
|
||||
downloaded above) to your JIRA `classes` folder. For self-contained
|
||||
JIRA installations, this will be `atlassian-jira/WEB-INF/classes/`,
|
||||
downloaded above) to your Jira `classes` folder. For self-contained
|
||||
Jira installations, this will be `atlassian-jira/WEB-INF/classes/`,
|
||||
but this may be different in your deployment.
|
||||
|
||||
2. Edit the constants at the top of
|
||||
|
@ -62,10 +62,10 @@ String issueBaseUrl = "https://jira.COMPANY.com/browse/"
|
|||
5. In the **Name of groovy class** field, enter
|
||||
`org.zulip.jira.ZulipListener`.
|
||||
|
||||
6. Click **Add Listener**, and JIRA will now notify your Zulip of
|
||||
changes to your issues! Updates from JIRA will be sent to the stream
|
||||
6. Click **Add Listener**, and Jira will now notify your Zulip of
|
||||
changes to your issues! Updates from Jira will be sent to the stream
|
||||
you've configured.
|
||||
|
||||
{!congrats.md!}
|
||||
|
||||
![JIRA bot message](/static/images/integrations/jira/001.png)
|
||||
![Jira bot message](/static/images/integrations/jira/001.png)
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
{% if find_team_link_disabled %}
|
||||
{% else %}
|
||||
<li>
|
||||
<a href="/accounts/find/">Find Accounts</a>
|
||||
<a href="/accounts/find/">Find accounts</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!-- Google / Search Engine Tags -->
|
||||
<!-- Google / search engine tags -->
|
||||
{% if allow_search_engine_indexing %}
|
||||
{% if OPEN_GRAPH_DESCRIPTION %}
|
||||
<meta name="description" content="{{ OPEN_GRAPH_DESCRIPTION }}" />
|
||||
|
@ -9,7 +9,7 @@
|
|||
<meta name="robots" content="noindex,nofollow" />
|
||||
{% endif %}
|
||||
|
||||
<!-- Open Graph / Facebook / Twitter Meta Tags -->
|
||||
<!-- Open Graph / Facebook / Twitter meta tags -->
|
||||
<meta property="og:url" content="{{ OPEN_GRAPH_URL }}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="Zulip" />
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{% set entrypoint = "landing-page" %}
|
||||
|
||||
{% block title %}
|
||||
<title>Zulip Security</title>
|
||||
<title>Zulip security</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block customhead %}
|
||||
|
@ -17,7 +17,7 @@
|
|||
<div class="hero bg-pycon security">
|
||||
<div class="bg-dimmer"></div>
|
||||
<div class="content">
|
||||
<h1 class="center">Zulip Security</h1>
|
||||
<h1 class="center">Zulip security</h1>
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -25,7 +25,7 @@ check_header() {
|
|||
if ! diff -ur /tmp/http-headers-processed "$success_header_file"; then
|
||||
set +x
|
||||
echo
|
||||
echo "FAILURE: The HTTP Headers returned from loading the homepage on the server do not match the contents of tools/ci/success-http-headers.template.txt. Typically, this means that the server threw a 500 when trying to load the homepage."
|
||||
echo "FAILURE: The HTTP headers returned from loading the homepage on the server do not match the contents of tools/ci/success-http-headers.template.txt. Typically, this means that the server threw a 500 when trying to load the homepage."
|
||||
echo "Displaying the contents of the server's error log:"
|
||||
echo
|
||||
cat /var/log/zulip/errors.log
|
||||
|
|
|
@ -40,7 +40,7 @@ ROBOTSTXT_OBEY = False
|
|||
# Disable cookies (enabled by default)
|
||||
# COOKIES_ENABLED = False
|
||||
|
||||
# Disable Telnet Console (enabled by default)
|
||||
# Disable telnet console (enabled by default)
|
||||
# TELNETCONSOLE_ENABLED = False
|
||||
|
||||
# Override the default request headers:
|
||||
|
|
|
@ -13,10 +13,10 @@ from scrapy.utils.url import url_has_any_extension
|
|||
from twisted.python.failure import Failure
|
||||
|
||||
EXCLUDED_DOMAINS = [
|
||||
# Returns 429 Rate-Limited Errors
|
||||
# Returns 429 rate-limiting errors
|
||||
"github.com",
|
||||
"gist.github.com",
|
||||
# Returns 503 Errors
|
||||
# Returns 503 errors
|
||||
"www.amazon.com",
|
||||
"gitlab.com",
|
||||
]
|
||||
|
|
|
@ -247,7 +247,7 @@ Your remote Zulip dev server has been created!
|
|||
print(
|
||||
"See [Developing remotely](https://zulip.readthedocs.io/en/latest/development/remote.html) "
|
||||
"for tips on using the remote dev instance and "
|
||||
"[Git & GitHub Guide](https://zulip.readthedocs.io/en/latest/git/index.html) "
|
||||
"[Git & GitHub guide](https://zulip.readthedocs.io/en/latest/git/index.html) "
|
||||
"to learn how to use Git with Zulip.\n"
|
||||
)
|
||||
print(
|
||||
|
|
|
@ -29,7 +29,6 @@ IGNORED_PHRASES = [
|
|||
r"ID",
|
||||
r"IDs",
|
||||
r"IP",
|
||||
r"JIRA",
|
||||
r"JSON",
|
||||
r"Kerberos",
|
||||
r"LDAP",
|
||||
|
|
|
@ -234,7 +234,7 @@ def validate_indent_html(fn: str, fix: bool) -> int:
|
|||
# Since we successfully fixed the issues, we exit with status 0
|
||||
return 0
|
||||
print(
|
||||
"Invalid Indentation detected in file: "
|
||||
"Invalid indentation detected in file: "
|
||||
f"{fn}\nDiff for the file against expected indented file:",
|
||||
flush=True,
|
||||
)
|
||||
|
|
|
@ -164,7 +164,7 @@ def tokenize(text: str) -> List[Token]:
|
|||
continue
|
||||
except TokenizationException as e:
|
||||
raise FormattedException(
|
||||
f'''{e.message} at Line {state.line} Col {state.col}:"{e.line_content}"''',
|
||||
f'''{e.message} at line {state.line} col {state.col}:"{e.line_content}"''',
|
||||
)
|
||||
|
||||
line_span = len(s.split("\n"))
|
||||
|
@ -401,9 +401,9 @@ def get_html_tag(text: str, i: int) -> str:
|
|||
end += 1
|
||||
if quote_count % 2 != 0:
|
||||
if unclosed_end:
|
||||
raise TokenizationException("Unbalanced Quotes", text[i:unclosed_end])
|
||||
raise TokenizationException("Unbalanced quotes", text[i:unclosed_end])
|
||||
else:
|
||||
raise TokenizationException("Unbalanced Quotes", text[i : end + 1])
|
||||
raise TokenizationException("Unbalanced quotes", text[i : end + 1])
|
||||
if end == len(text) or text[end] != ">":
|
||||
raise TokenizationException('Tag missing ">"', text[i : end + 1])
|
||||
s = text[i : end + 1]
|
||||
|
|
|
@ -180,7 +180,7 @@ def run() -> None:
|
|||
[*semgrep_command, "--lang=python"],
|
||||
["py"],
|
||||
fix_arg="--autofix",
|
||||
description="Syntactic Grep (semgrep) Code Search Tool (config: ./tools/semgrep.yml)",
|
||||
description="Syntactic grep (semgrep) code search tool (config: ./tools/semgrep.yml)",
|
||||
)
|
||||
|
||||
linter_config.external_linter(
|
||||
|
|
|
@ -533,7 +533,7 @@ html_rules: List["Rule"] = [
|
|||
*prose_style_rules,
|
||||
{
|
||||
"pattern": "subject|SUBJECT",
|
||||
"exclude": {"templates/zerver/email.html"},
|
||||
"exclude": {"templates/zerver/email.html", "zerver/tests/fixtures/email"},
|
||||
"exclude_pattern": "email subject",
|
||||
"description": "avoid subject in templates",
|
||||
"good_lines": ["topic_name"],
|
||||
|
|
|
@ -19,7 +19,7 @@ elif [[ "$MODE" == "test" ]]; then
|
|||
DBNAME=zulip_test
|
||||
STATUS_FILE_NAME=migration_status_test
|
||||
else
|
||||
echo "Usage Instructions"
|
||||
echo "Usage instructions"
|
||||
echo "Run with either no arguments (sets up devel db for local deploy--zulip with user zulip)"
|
||||
echo "or specify 'test'"
|
||||
exit 1
|
||||
|
@ -52,7 +52,7 @@ DBNAME_BASE=${DBNAME}_base
|
|||
if ! pg_isready -U "$POSTGRES_USER" -q; then
|
||||
set +x
|
||||
echo
|
||||
echo 'ERROR: PostgreSQL Server is not running! Ensure the service is enabled.'
|
||||
echo 'ERROR: PostgreSQL server is not running! Ensure the service is enabled.'
|
||||
# shellcheck disable=SC2016
|
||||
echo 'ERROR: Try `sudo service postgresql start`?'
|
||||
echo "ERROR: You can easily test if you fixed it using: pg_isready -U \$POSTGRES_USER"
|
||||
|
|
|
@ -56,7 +56,7 @@ def check_worker_launch(run_dev: "subprocess.Popen[str]") -> bool:
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\nStarting Development Server")
|
||||
print("\nStarting development server")
|
||||
args = [f"{TOOLS_DIR}/run-dev.py"]
|
||||
run_dev = subprocess.Popen(
|
||||
args,
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<body>
|
||||
<div id="intro">
|
||||
<p id="intro">
|
||||
<span id="hello_{{ message }}">Hello World!!</span>
|
||||
<span id="hello_{{ message }}">Hello world!!</span>
|
||||
This is a test file for checking correct working of duplicate id detection module.
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<body>
|
||||
<div id="below_navbar">
|
||||
<p id="intro">
|
||||
<span id="hello_{{ message }}">Hello World!!</span>
|
||||
<span id="hello_{{ message }}">Hello world!!</span>
|
||||
This is a test file for checking correct working of duplicate id detection module.
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -106,7 +106,7 @@ class ParserTest(unittest.TestCase):
|
|||
{{# foo
|
||||
"""
|
||||
self._assert_validate_error(
|
||||
'''Tag missing "}}" at Line 2 Col 13:"{{# foo
|
||||
'''Tag missing "}}" at line 2 col 13:"{{# foo
|
||||
"''',
|
||||
text=my_html,
|
||||
)
|
||||
|
@ -115,14 +115,14 @@ class ParserTest(unittest.TestCase):
|
|||
my_html = """
|
||||
{{# foo }
|
||||
"""
|
||||
self._assert_validate_error('Tag missing "}}" at Line 2 Col 13:"{{# foo }\n"', text=my_html)
|
||||
self._assert_validate_error('Tag missing "}}" at line 2 col 13:"{{# foo }\n"', text=my_html)
|
||||
|
||||
def test_validate_incomplete_django_tag_1(self) -> None:
|
||||
my_html = """
|
||||
{% foo
|
||||
"""
|
||||
self._assert_validate_error(
|
||||
'''Tag missing "%}" at Line 2 Col 13:"{% foo
|
||||
'''Tag missing "%}" at line 2 col 13:"{% foo
|
||||
"''',
|
||||
text=my_html,
|
||||
)
|
||||
|
@ -131,14 +131,14 @@ class ParserTest(unittest.TestCase):
|
|||
my_html = """
|
||||
{% foo %
|
||||
"""
|
||||
self._assert_validate_error('Tag missing "%}" at Line 2 Col 13:"{% foo %\n"', text=my_html)
|
||||
self._assert_validate_error('Tag missing "%}" at line 2 col 13:"{% foo %\n"', text=my_html)
|
||||
|
||||
def test_validate_incomplete_html_tag_1(self) -> None:
|
||||
my_html = """
|
||||
<b
|
||||
"""
|
||||
self._assert_validate_error(
|
||||
'''Tag missing ">" at Line 2 Col 13:"<b
|
||||
'''Tag missing ">" at line 2 col 13:"<b
|
||||
"''',
|
||||
text=my_html,
|
||||
)
|
||||
|
@ -151,12 +151,12 @@ class ParserTest(unittest.TestCase):
|
|||
<a href=""
|
||||
"""
|
||||
self._assert_validate_error(
|
||||
'''Tag missing ">" at Line 2 Col 13:"<a href=""
|
||||
'''Tag missing ">" at line 2 col 13:"<a href=""
|
||||
"''',
|
||||
text=my_html1,
|
||||
)
|
||||
self._assert_validate_error(
|
||||
'''Unbalanced Quotes at Line 2 Col 13:"<a href="
|
||||
'''Unbalanced quotes at line 2 col 13:"<a href="
|
||||
"''',
|
||||
text=my_html,
|
||||
)
|
||||
|
|
|
@ -257,7 +257,7 @@ def convert_gitter_workspace_messages(
|
|||
message_json["zerver_message"] = zerver_message
|
||||
message_json["zerver_usermessage"] = zerver_usermessage
|
||||
message_filename = os.path.join(output_dir, f"messages-{dump_file_id:06}.json")
|
||||
logging.info("Writing Messages to %s\n", message_filename)
|
||||
logging.info("Writing messages to %s\n", message_filename)
|
||||
write_data_to_file(os.path.join(message_filename), message_json)
|
||||
|
||||
low_index = upper_index
|
||||
|
|
|
@ -738,7 +738,7 @@ def convert_slack_workspace_messages(
|
|||
message_json = dict(zerver_message=zerver_message, zerver_usermessage=zerver_usermessage)
|
||||
|
||||
message_file = f"/messages-{dump_file_id:06}.json"
|
||||
logging.info("Writing Messages to %s\n", output_dir + message_file)
|
||||
logging.info("Writing messages to %s\n", output_dir + message_file)
|
||||
create_converted_data_files(message_json, output_dir, message_file)
|
||||
|
||||
total_reactions += reactions
|
||||
|
|
|
@ -27,8 +27,8 @@ SLACK_MAILTO_REGEX = r"""
|
|||
SLACK_USERMENTION_REGEX = r"""
|
||||
(<@) # Start with '<@'
|
||||
([a-zA-Z0-9]+) # Here we have the Slack id
|
||||
(\|)? # We not always have a Vertical line in mention
|
||||
([a-zA-Z0-9]+)? # If Vertical line is present, this is short name
|
||||
(\|)? # We not always have a vertical line in mention
|
||||
([a-zA-Z0-9]+)? # If vertical line is present, this is short name
|
||||
(>) # ends with '>'
|
||||
"""
|
||||
# Slack doesn't have mid-word message-formatting like Zulip.
|
||||
|
|
|
@ -784,8 +784,8 @@ def client_is_exempt_from_rate_limiting(request: HttpRequest) -> bool:
|
|||
|
||||
|
||||
def internal_notify_view(is_tornado_view: bool) -> Callable[[ViewFuncT], ViewFuncT]:
|
||||
# The typing here could be improved by using the Extended Callable types:
|
||||
# https://mypy.readthedocs.io/en/latest/kinds_of_types.html#extended-callable-types
|
||||
# The typing here could be improved by using the extended Callable types:
|
||||
# https://mypy.readthedocs.io/en/stable/additional_features.html#extended-callable-types
|
||||
"""Used for situations where something running on the Zulip server
|
||||
needs to make a request to the (other) Django/Tornado processes running on
|
||||
the server."""
|
||||
|
|
|
@ -3059,7 +3059,7 @@ def check_message(
|
|||
|
||||
check_private_message_policy(realm, sender, user_profiles)
|
||||
|
||||
# API Super-users who set the `forged` flag are allowed to
|
||||
# API super-users who set the `forged` flag are allowed to
|
||||
# forge messages sent by any user, so we disable the
|
||||
# `forwarded_mirror_message` security check in that case.
|
||||
forwarded_mirror_message = mirror_message and not forged
|
||||
|
@ -3651,7 +3651,7 @@ def bulk_add_subs_to_db_with_logging(
|
|||
sub_ids = [info.sub.id for info in subs_to_activate]
|
||||
Subscription.objects.filter(id__in=sub_ids).update(active=True)
|
||||
|
||||
# Log Subscription Activities in RealmAuditLog
|
||||
# Log subscription activities in RealmAuditLog
|
||||
event_time = timezone_now()
|
||||
event_last_message_id = get_last_message_id()
|
||||
|
||||
|
@ -3888,7 +3888,7 @@ def bulk_remove_subscriptions(
|
|||
).update(active=False)
|
||||
occupied_streams_after = list(get_occupied_streams(our_realm))
|
||||
|
||||
# Log Subscription Activities in RealmAuditLog
|
||||
# Log subscription activities in RealmAuditLog
|
||||
event_time = timezone_now()
|
||||
event_last_message_id = get_last_message_id()
|
||||
all_subscription_logs = [
|
||||
|
|
|
@ -375,7 +375,7 @@ CacheItemT = TypeVar("CacheItemT")
|
|||
# serializable objects, will be the object; if encoded, bytes.
|
||||
CompressedItemT = TypeVar("CompressedItemT")
|
||||
|
||||
# Required Arguments are as follows:
|
||||
# Required arguments are as follows:
|
||||
# * object_ids: The list of object ids to look up
|
||||
# * cache_key_function: object_id => cache key
|
||||
# * query_function: [object_ids] => [objects from database]
|
||||
|
|
|
@ -1184,7 +1184,7 @@ def write_message_partial_for_query(
|
|||
# Figure out the name of our shard file.
|
||||
message_filename = os.path.join(output_dir, f"messages-{dump_file_id:06}.json")
|
||||
message_filename += ".partial"
|
||||
logging.info("Fetched Messages for %s", message_filename)
|
||||
logging.info("Fetched messages for %s", message_filename)
|
||||
|
||||
# Clean up our messages.
|
||||
table_data: TableData = {}
|
||||
|
@ -1538,7 +1538,7 @@ def export_emoji_from_local(realm: Realm, local_dir: Path, output_dir: Path) ->
|
|||
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
shutil.copy2(local_path, output_path)
|
||||
# Realm Emoji author is optional.
|
||||
# Realm emoji author is optional.
|
||||
author = realm_emoji.author
|
||||
author_id = None
|
||||
if author:
|
||||
|
@ -1830,7 +1830,7 @@ def export_messages_single_user(
|
|||
message_chunk.append(item)
|
||||
|
||||
message_filename = os.path.join(output_dir, f"messages-{dump_file_id:06}.json")
|
||||
logging.info("Fetched Messages for %s", message_filename)
|
||||
logging.info("Fetched messages for %s", message_filename)
|
||||
|
||||
output = {"zerver_message": message_chunk}
|
||||
floatify_datetime_fields(output, "zerver_message")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
This module stores data for "External Account" custom profile field.
|
||||
This module stores data for "external account" custom profile field.
|
||||
"""
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext as _
|
||||
|
|
|
@ -864,7 +864,7 @@ def import_uploads(
|
|||
#
|
||||
# Because the Python object => JSON conversion process is not fully
|
||||
# faithful, we have to use a set of fixers (e.g. on DateTime objects
|
||||
# and Foreign Keys) to do the import correctly.
|
||||
# and foreign keys) to do the import correctly.
|
||||
def do_import_realm(import_dir: Path, subdomain: str, processes: int = 1) -> Realm:
|
||||
logging.info("Importing realm dump %s", import_dir)
|
||||
if not os.path.exists(import_dir):
|
||||
|
|
|
@ -404,9 +404,9 @@ WEBHOOK_INTEGRATIONS: List[WebhookIntegration] = [
|
|||
),
|
||||
WebhookIntegration("insping", ["monitoring"], display_name="Insping"),
|
||||
WebhookIntegration("intercom", ["customer-support"], display_name="Intercom"),
|
||||
WebhookIntegration("jira", ["project-management"], display_name="JIRA"),
|
||||
WebhookIntegration("jira", ["project-management"], display_name="Jira"),
|
||||
WebhookIntegration("jotform", ["misc"], display_name="Jotform"),
|
||||
WebhookIntegration("json", ["misc"], display_name="JSON Formatter"),
|
||||
WebhookIntegration("json", ["misc"], display_name="JSON formatter"),
|
||||
WebhookIntegration("librato", ["monitoring"]),
|
||||
WebhookIntegration("mention", ["marketing"], display_name="Mention"),
|
||||
WebhookIntegration("netlify", ["continuous-integration", "deployment"], display_name="Netlify"),
|
||||
|
@ -532,7 +532,7 @@ INTEGRATIONS: Dict[str, Integration] = {
|
|||
["project-management"],
|
||||
logo="images/integrations/logos/jira.svg",
|
||||
secondary_line_text="(locally installed)",
|
||||
display_name="JIRA",
|
||||
display_name="Jira",
|
||||
doc="zerver/integrations/jira-plugin.md",
|
||||
stream_name="jira",
|
||||
legacy=True,
|
||||
|
@ -801,7 +801,7 @@ DOC_SCREENSHOT_CONFIG: Dict[str, List[BaseScreenshotConfig]] = {
|
|||
extra_params={
|
||||
"ticket_title": "Test ticket",
|
||||
"ticket_id": "4",
|
||||
"message": "Test Message",
|
||||
"message": "Test message",
|
||||
},
|
||||
)
|
||||
],
|
||||
|
|
|
@ -447,7 +447,7 @@ def send_custom_email(users: List[UserProfile], options: Dict[str, Any]) -> None
|
|||
Can be used directly with from a management shell with
|
||||
send_custom_email(user_profile_list, dict(
|
||||
markdown_template_path="/path/to/markdown/file.md",
|
||||
subject="Email Subject",
|
||||
subject="Email subject",
|
||||
from_name="Sender Name")
|
||||
)
|
||||
"""
|
||||
|
|
|
@ -147,7 +147,7 @@ def add_missing_messages(user_profile: UserProfile) -> None:
|
|||
).values("recipient_id", "recipient__type_id")
|
||||
)
|
||||
|
||||
# For Stream messages we need to check messages against data from
|
||||
# For stream messages we need to check messages against data from
|
||||
# RealmAuditLog for visibility to user. So we fetch the subscription logs.
|
||||
stream_ids = [sub["recipient__type_id"] for sub in all_stream_subs]
|
||||
events = [
|
||||
|
@ -305,7 +305,7 @@ def reactivate_user_if_soft_deactivated(user_profile: UserProfile) -> Union[User
|
|||
event_type=RealmAuditLog.USER_SOFT_ACTIVATED,
|
||||
event_time=timezone_now(),
|
||||
)
|
||||
logger.info("Soft Reactivated user %s", user_profile.id)
|
||||
logger.info("Soft reactivated user %s", user_profile.id)
|
||||
return user_profile
|
||||
return None
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ def check_send_webhook_message(
|
|||
assert user_profile.bot_owner is not None
|
||||
check_send_private_message(user_profile, request.client, user_profile.bot_owner, body)
|
||||
else:
|
||||
# Some third-party websites (such as Atlassian's JIRA), tend to
|
||||
# Some third-party websites (such as Atlassian's Jira), tend to
|
||||
# double escape their URLs in a manner that escaped space characters
|
||||
# (%20) are never properly decoded. We work around that by making sure
|
||||
# that the URL parameters are decoded on our end.
|
||||
|
|
|
@ -240,7 +240,7 @@ def get_issue_event_message(
|
|||
message=message,
|
||||
assignee=assignee,
|
||||
assignees=assignees,
|
||||
type="Issue",
|
||||
type="issue",
|
||||
title=title,
|
||||
)
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ class Command(ZulipBaseCommand):
|
|||
|
||||
users_to_activate = get_users_from_emails(user_emails, filter_kwargs)
|
||||
users_activated = do_soft_activate_users(users_to_activate)
|
||||
logger.info("Soft Reactivated %d user(s)", len(users_activated))
|
||||
logger.info("Soft reactivated %d user(s)", len(users_activated))
|
||||
|
||||
elif deactivate:
|
||||
if user_emails:
|
||||
|
|
|
@ -475,7 +475,7 @@ class CsrfFailureError(JsonableError):
|
|||
|
||||
@staticmethod
|
||||
def msg_format() -> str:
|
||||
return _("CSRF Error: {reason}")
|
||||
return _("CSRF error: {reason}")
|
||||
|
||||
|
||||
def csrf_failure(request: HttpRequest, reason: str = "") -> HttpResponse:
|
||||
|
|
|
@ -1692,7 +1692,7 @@ class PreregistrationUser(models.Model):
|
|||
email: str = models.EmailField()
|
||||
|
||||
# If the pre-registration process provides a suggested full name for this user,
|
||||
# store it here to use it to prepopulate the Full Name field in the registration form:
|
||||
# store it here to use it to prepopulate the full name field in the registration form:
|
||||
full_name: Optional[str] = models.CharField(max_length=UserProfile.MAX_NAME_LENGTH, null=True)
|
||||
full_name_validated: bool = models.BooleanField(default=False)
|
||||
referred_by: Optional[UserProfile] = models.ForeignKey(
|
||||
|
@ -3502,7 +3502,7 @@ class CustomProfileField(models.Model):
|
|||
}
|
||||
|
||||
FIELD_TYPE_DATA: List[FieldElement] = [
|
||||
# Type, Display Name, Validator, Converter, Keyword
|
||||
# Type, display name, validator, converter, keyword
|
||||
(SHORT_TEXT, gettext_lazy("Short text"), check_short_string, str, "SHORT_TEXT"),
|
||||
(LONG_TEXT, gettext_lazy("Long text"), check_long_string, str, "LONG_TEXT"),
|
||||
(DATE, gettext_lazy("Date picker"), check_date, str, "DATE"),
|
||||
|
|
|
@ -350,7 +350,7 @@ def reorder_realm_profile_fields(client: Client) -> None:
|
|||
def create_realm_profile_field(client: Client) -> None:
|
||||
# {code_example|start}
|
||||
# Create a custom profile field in the user's organization.
|
||||
request = {"name": "Phone", "hint": "Contact No.", "field_type": 1}
|
||||
request = {"name": "Phone", "hint": "Contact no.", "field_type": 1}
|
||||
|
||||
result = client.call_endpoint(url="/realm/profile_fields", method="POST", request=request)
|
||||
# {code_example|end}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue