mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
Compare commits
132 Commits
3.1-with-b
...
2.1.3
Author | SHA1 | Date | |
---|---|---|---|
|
d22cb7d01f | ||
|
76ce370181 | ||
|
64856d858e | ||
|
c9796ba7f7 | ||
|
b21117954d | ||
|
59f5ca713f | ||
|
67da8e8431 | ||
|
b79fbf9239 | ||
|
f1f937e4ea | ||
|
68628149db | ||
|
f247721a2d | ||
|
e3d6b4f210 | ||
|
ea8e6149da | ||
|
376cd88a83 | ||
|
bfd92260fd | ||
|
217431d0c4 | ||
|
30cc6798b3 | ||
|
677ad69555 | ||
|
95118d860d | ||
|
b8888c801b | ||
|
7a9251a3e1 | ||
|
64ec413940 | ||
|
147c3998de | ||
|
79fc9c3281 | ||
|
a33d7f0400 | ||
|
2471f6ad83 | ||
|
19d1ca3a1d | ||
|
9fcbc3a49b | ||
|
1413fda773 | ||
|
494e596be8 | ||
|
4cc25f8e84 | ||
|
19ab295172 | ||
|
31f02cd926 | ||
|
266c7c83e0 | ||
|
dd198fd06e | ||
|
10e8928b0f | ||
|
bc81275d3c | ||
|
6c8c3cd3dc | ||
|
1783515794 | ||
|
21026d984b | ||
|
66fe724c8a | ||
|
282d6edf2e | ||
|
785a7ec9e7 | ||
|
c44d9f9b1b | ||
|
0d5d3c4912 | ||
|
ef793590c1 | ||
|
3032ba15cf | ||
|
96a2ddffe7 | ||
|
2794362214 | ||
|
9b3e1e2c97 | ||
|
ae44fdd7cc | ||
|
b45cce61e7 | ||
|
2e923a0eb5 | ||
|
f538f34d95 | ||
|
5d2befdc54 | ||
|
cc8b83b261 | ||
|
ac8f4aaa93 | ||
|
843c148c59 | ||
|
d39bcf2264 | ||
|
ce64a6b163 | ||
|
7875196783 | ||
|
56c1ad1a3d | ||
|
d9aa4161f8 | ||
|
728155afee | ||
|
660501c782 | ||
|
ad974c3ae3 | ||
|
bc4029deae | ||
|
218ca61dd0 | ||
|
3419908f39 | ||
|
af67990f14 | ||
|
e6cf30fc22 | ||
|
e2ccbe7c80 | ||
|
8b31387670 | ||
|
501eb09716 | ||
|
280d9db26d | ||
|
cee6227f53 | ||
|
cae803e8a9 | ||
|
ba598366e9 | ||
|
d452ad31e0 | ||
|
aed813f44c | ||
|
71dae1b92a | ||
|
629ec1aa8b | ||
|
87d60a1fff | ||
|
98eef54e4f | ||
|
235ba339d0 | ||
|
e5320cc1f6 | ||
|
1d72ea2fd5 | ||
|
c7948a7960 | ||
|
04bb26be3a | ||
|
7f45ca9b22 | ||
|
1bedb965e9 | ||
|
bc752188e7 | ||
|
b0ea81fe16 | ||
|
358ab821c4 | ||
|
97322dd195 | ||
|
1ba48a04da | ||
|
e8377b605f | ||
|
830f1e9f3f | ||
|
037b87b580 | ||
|
82a6e77301 | ||
|
9efb90510c | ||
|
b255c8b8a6 | ||
|
03e8e8be9d | ||
|
2932d9cd28 | ||
|
0baa205ad3 | ||
|
a8d8500c46 | ||
|
aa19f43f0b | ||
|
0974b0130d | ||
|
8a1d2bb5b6 | ||
|
a38976f25d | ||
|
fccfc02981 | ||
|
929847ae2d | ||
|
a3338f3735 | ||
|
f377ef6dd7 | ||
|
4c9997a523 | ||
|
2470fba95c | ||
|
2a6145f7fb | ||
|
7036fea97b | ||
|
05a42fb8df | ||
|
cd0b14ce2f | ||
|
a1fc8fb079 | ||
|
e147ee2087 | ||
|
61180020c1 | ||
|
2a473c57f4 | ||
|
c0980e3e9e | ||
|
035d4c57be | ||
|
fcbd24e72c | ||
|
29babba85a | ||
|
49ff894d6a | ||
|
f3e75b6b5f | ||
|
6b9f37dc8f | ||
|
cd926b8aae |
@@ -39,7 +39,7 @@ something goes wrong, this helps you figure out when and why.
|
||||
|
||||
If you don't already have one installed, here are some suggestions:
|
||||
|
||||
- macOS: [GitX-dev][gitgui-gitxdev]
|
||||
- macOS: [GitX][gitgui-gitx] (previously [GitX-dev][gitgui-gitxdev])
|
||||
- Ubuntu/Linux: [git-cola][gitgui-gitcola], [gitg][gitgui-gitg], [gitk][gitgui-gitk]
|
||||
- Windows: [SourceTree][gitgui-sourcetree]
|
||||
|
||||
@@ -61,6 +61,7 @@ And, if none of the above are to your liking, try [one of these][gitbook-guis].
|
||||
[gitgui-gitcola]: http://git-cola.github.io/
|
||||
[gitgui-gitg]: https://wiki.gnome.org/Apps/Gitg
|
||||
[gitgui-gitk]: https://git-scm.com/docs/gitk
|
||||
[gitgui-gitx]: https://github.com/gitx/gitx/
|
||||
[gitgui-gitxdev]: https://rowanj.github.io/gitx/
|
||||
[gitgui-sourcetree]: https://www.sourcetreeapp.com/
|
||||
[github-help-add-ssh-key]: https://help.github.com/en/articles/adding-a-new-ssh-key-to-your-github-account
|
||||
|
@@ -7,6 +7,69 @@ All notable changes to the Zulip server are documented in this file.
|
||||
This section lists notable unreleased changes; it is generally updated
|
||||
in bursts.
|
||||
|
||||
### 2.1.3 -- 2020-04-01
|
||||
|
||||
- CVE-2020-9444: Prevent reverse tabnapping attacks.
|
||||
- CVE-2020-9445: Remove unused and insecure modal_link feature.
|
||||
- CVE-2020-10935: Fix XSS vulnerability in local link rewriting.
|
||||
- Blocked access from Zulip Desktop versions below 5.0.0. This
|
||||
behavior can be adjusted by editing `DESKTOP_*_VERSION`
|
||||
in `/home/zulip/deployments/current/version.py`.
|
||||
- Restructured server initialization to simplify initialization of
|
||||
Docker containers (eliminating common classes of user error).
|
||||
- Removed buggy feedback bot (`ENABLE_FEEDBACK`).
|
||||
- Migrated GitHub authentication to use the current encoding.
|
||||
- Fixed support for restoring a backup on a different minor release
|
||||
(in the common case they have the same database schema).
|
||||
- Fixed restoring backups with memcached authentication enabled.
|
||||
- Fixed preview content (preheaders) for many emails.
|
||||
- Fixed buggy text in missed-message emails with PM content disabled.
|
||||
- Fixed buggy loading spinner in "emoji format" widget.
|
||||
- Fixed sorting and filtering users in organization settings.
|
||||
- Fixed handling of links to deleted streams.
|
||||
- Fixed check-rabbitmq-consumers monitoring.
|
||||
- Fixed copy-to-clipboard button for outgoing webhook bots.
|
||||
- Fixed logging spam from soft_deactivation cron job.
|
||||
- Fixed email integration handling of emails with nested MIME structure.
|
||||
- Fixed unicode bugs in incoming email integration.
|
||||
- Fixed error handling for Slack data import.
|
||||
- Fixed incoming webhook support for AWX 9.x.y.
|
||||
- Fixed a couple missing translation tags.
|
||||
- Fixed "User groups" settings UI bug for administrators.
|
||||
- Fixed data import tool to reset resource limits after importing
|
||||
data from a free plan organization on zulipchat.com.
|
||||
- Changed the SAML default signature algorithm to SHA-256, overriding
|
||||
the SHA-1 default used by python3-saml.
|
||||
|
||||
### 2.1.2 -- 2020-01-16
|
||||
|
||||
- Corrected fix for CVE-2019-19775 (the original fix was affected by
|
||||
an unfixed security bug in Python's urllib, CVE-2015-2104).
|
||||
- Migrated data for handling replies to missed-message emails from
|
||||
semi-persistent redis to the fully persistent database.
|
||||
- Added authentication for redis and memcached even in configurations
|
||||
where these are running on localhost, for add hardening against
|
||||
attacks from malicious processes running on the Zulip server.
|
||||
- Improved logging for misconfigurations of LDAP authentication.
|
||||
- Improved error handling for invalid LDAP configurations.
|
||||
- Improved error tracebacks for invalid memcached keys.
|
||||
- Fixed support for using LDAP with email address visibility
|
||||
limited to administrators.
|
||||
- Fixed styling of complex markup within /me messages.
|
||||
- Fixed left sidebar duplicating some group private message threads.
|
||||
- Fixed the "Mentions" narrow being unable to mark messages as read.
|
||||
- Fixed error handling bug preventing rerunning the installer.
|
||||
- Fixed a few minor issues with migrations for upgrading from 2.0.x.
|
||||
|
||||
### 2.1.1 -- 2019-12-13
|
||||
|
||||
- Fixed upgrading to 2.1.x with the LDAP integration enabled in a
|
||||
configuration where `AUTH_LDAP_REVERSE_EMAIL_SEARCH` is newly
|
||||
required, but is not set yet.
|
||||
- Reimplemented --postgres-missing-dictionaries installer option,
|
||||
used with our new support for a DBaaS managed database.
|
||||
- Improved documentation for `AUTH_LDAP_REVERSE_EMAIL_SEARCH`.
|
||||
|
||||
### 2.1.0 -- 2019-12-12
|
||||
|
||||
**Highlights:**
|
||||
|
@@ -51,7 +51,7 @@ it as follows:
|
||||
The `Entity ID` should match the value of
|
||||
`SOCIAL_AUTH_SAML_SP_ENTITY_ID` computed in the Zulip settings.
|
||||
You can get the correct value by running the following:
|
||||
`/home/zulip/deployments/current/scripts/setup/get-django-setting
|
||||
`/home/zulip/deployments/current/scripts/get-django-setting
|
||||
SOCIAL_AUTH_SAML_SP_ENTITY_ID`.
|
||||
|
||||
* **SSO URL**:
|
||||
@@ -176,29 +176,33 @@ In either configuration, you will need to do the following:
|
||||
the form it needs for authentication. There are three supported
|
||||
ways to set up the username and/or email mapping:
|
||||
|
||||
(A) Using email addresses as usernames, if LDAP has each user's
|
||||
email address. To do this, just set `AUTH_LDAP_USER_SEARCH` to
|
||||
query by email address.
|
||||
(A) Using email addresses as Zulip usernames, if LDAP has each
|
||||
user's email address:
|
||||
* Make `AUTH_LDAP_USER_SEARCH` a query by email address.
|
||||
* Set `AUTH_LDAP_REVERSE_EMAIL_SEARCH` to the same query with
|
||||
`%(email)s` rather than `%(user)s` as the search parameter.
|
||||
* Set `AUTH_LDAP_USERNAME_ATTR` to the name of the LDAP
|
||||
attribute for the user's LDAP username in the search result
|
||||
for `AUTH_LDAP_REVERSE_EMAIL_SEARCH`.
|
||||
|
||||
(B) Using LDAP usernames as Zulip usernames, with email addresses
|
||||
formed consistently like `sam` -> `sam@example.com`. To do
|
||||
this, set `AUTH_LDAP_USER_SEARCH` to query by LDAP username, and
|
||||
`LDAP_APPEND_DOMAIN = "example.com"`.
|
||||
formed consistently like `sam` -> `sam@example.com`:
|
||||
* Set `AUTH_LDAP_USER_SEARCH` to query by LDAP username
|
||||
* Set `LDAP_APPEND_DOMAIN = "example.com"`.
|
||||
|
||||
(C) Using LDAP usernames as Zulip usernames, with email addresses
|
||||
taken from some other attribute in LDAP (for example, `email`).
|
||||
To do this, set `AUTH_LDAP_USER_SEARCH` to query by LDAP
|
||||
username, and `LDAP_EMAIL_ATTR = "email"`.
|
||||
|
||||
1. In configurations (A) and (C), you need to tell Zulip how to look
|
||||
up a user's LDAP data given their user's email address:
|
||||
|
||||
* Set `AUTH_LDAP_REVERSE_EMAIL_SEARCH` to a query that will find an
|
||||
LDAP user given their email address. Generally, this will be
|
||||
`AUTH_LDAP_USER_SEARCH` in configuration (A) or a search by
|
||||
`LDAP_EMAIL_ATTR` in configuration (C).
|
||||
* Set `AUTH_LDAP_USERNAME_ATTR` to the name of the LDAP attribute
|
||||
for the user's LDAP username in that search result.
|
||||
taken from some other attribute in LDAP (for example, `mail`):
|
||||
* Set `AUTH_LDAP_USER_SEARCH` to query by LDAP username
|
||||
* Set `LDAP_EMAIL_ATTR = "mail"`.
|
||||
* Set `AUTH_LDAP_REVERSE_EMAIL_SEARCH` to a query that will find
|
||||
an LDAP user given their email address (i.e. a search by
|
||||
`LDAP_EMAIL_ATTR`). For example:
|
||||
```
|
||||
AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
|
||||
ldap.SCOPE_SUBTREE, "(mail=%(email)s)")
|
||||
```
|
||||
* Set `AUTH_LDAP_USERNAME_ATTR` to the name of the LDAP
|
||||
attribute for the user's LDAP username in that search result.
|
||||
|
||||
You can quickly test whether your configuration works by running:
|
||||
|
||||
@@ -210,28 +214,42 @@ from the root of your Zulip installation. If your configuration is
|
||||
working, that will output the full name for your user (and that user's
|
||||
email address, if it isn't the same as the "Zulip username").
|
||||
|
||||
**Active Directory**: For Active Directory, one typically sets
|
||||
`AUTH_LDAP_USER_SEARCH` to one of:
|
||||
**Active Directory**: Most Active Directory installations will use one
|
||||
of the following configurations:
|
||||
|
||||
* To access by Active Directory username:
|
||||
```
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
|
||||
ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)")
|
||||
AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
|
||||
ldap.SCOPE_SUBTREE, "(mail=%(email)s)")
|
||||
AUTH_LDAP_USERNAME_ATTR = "sAMAccountName"
|
||||
```
|
||||
|
||||
* To access by Active Directory email address:
|
||||
```
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
|
||||
ldap.SCOPE_SUBTREE, "(mail=%(user)s)")
|
||||
AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
|
||||
ldap.SCOPE_SUBTREE, "(mail=%(email)s)")
|
||||
AUTH_LDAP_USERNAME_ATTR = "mail"
|
||||
```
|
||||
|
||||
**If you are using LDAP for authentication**: you will need to enable
|
||||
the `zproject.backends.ZulipLDAPAuthBackend` auth backend, in
|
||||
`AUTHENTICATION_BACKENDS` in `/etc/zulip/settings.py`. After doing
|
||||
so (and as always [restarting the Zulip server](settings.md) to ensure
|
||||
`AUTHENTICATION_BACKENDS` in `/etc/zulip/settings.py`. After doing so
|
||||
(and as always [restarting the Zulip server](settings.md) to ensure
|
||||
your settings changes take effect), you should be able to log into
|
||||
Zulip by entering your email address and LDAP password on the Zulip
|
||||
login form.
|
||||
|
||||
You may also want to configure Zulip's settings for [inviting new
|
||||
users](https://zulipchat.com/help/invite-new-users). If LDAP is the
|
||||
only enabled authentication method, the main use case for Zulip's
|
||||
invitation feature is selecting the initial streams for invited users
|
||||
(invited users will still need to use their LDAP password to create an
|
||||
account).
|
||||
|
||||
### Synchronizing data
|
||||
|
||||
Zulip can automatically synchronize data declared in
|
||||
@@ -368,6 +386,26 @@ details.
|
||||
|
||||
[upstream-ldap-groups]: https://django-auth-ldap.readthedocs.io/en/latest/groups.html#limiting-access
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Most issues with LDAP authentication are caused by misconfigurations of
|
||||
the user and email search settings. Some things you can try to get to
|
||||
the bottom of the problem:
|
||||
|
||||
* Review the instructions for the LDAP configuration type you're
|
||||
using: (A), (B) or (C) (described above), and that you have
|
||||
configured all of the required settings documented in the
|
||||
instructions for that configuration type.
|
||||
* Use the `manage.py query_ldap` tool to verify your configuration.
|
||||
The output of the command will usually indicate the cause of any
|
||||
configuration problem. For the LDAP integration to work, this
|
||||
command should be able to successfully fetch a complete, correct set
|
||||
of data for the queried user.
|
||||
* You can find LDAP-specific logs in `/var/log/zulip/ldap.log`. If
|
||||
you're asking for help with your setup, please provide logs from
|
||||
this file (feel free to anonymize any email addresses to
|
||||
`username@example.com`) in your report.
|
||||
|
||||
## Apache-based SSO with `REMOTE_USER`
|
||||
|
||||
If you have any existing SSO solution where a preferred way to deploy
|
||||
|
@@ -134,14 +134,11 @@ follows:
|
||||
postgres_password = abcd1234
|
||||
```
|
||||
|
||||
Now complete the installation by running the following command to ask
|
||||
the Zulip installer to initialize the postgres database. (Note: The
|
||||
options are different from before).
|
||||
Now complete the installation by running the following commands.
|
||||
|
||||
```
|
||||
./zulip-server-*/scripts/setup/install --certbot \
|
||||
--email=YOUR_EMAIL --hostname=YOUR_HOSTNAME \
|
||||
--remote-postgres --postgres-missing-dictionaries
|
||||
# Ask Zulip installer to initialize the postgres database.
|
||||
su zulip -c '/home/zulip/deployments/current/scripts/setup/initialize-database'
|
||||
|
||||
# And then generate a realm creation link:
|
||||
su zulip -c '/home/zulip/deployments/current/manage.py generate_realm_creation_link'
|
||||
|
@@ -62,7 +62,7 @@ find the service's provided "SMTP credentials", and configure Zulip as
|
||||
follows:
|
||||
|
||||
* The hostname like `EMAIL_HOST = 'smtp.mailgun.org'` in `/etc/zulip/settings.py`
|
||||
* The username like `EMAIL_HOST_USER = 'username@example.com` in
|
||||
* The username like `EMAIL_HOST_USER = 'username@example.com'` in
|
||||
`/etc/zulip/settings.py`.
|
||||
* The TLS setting as `EMAIL_USE_TLS = True` in
|
||||
`/etc/zulip/settings.py`, for most providers
|
||||
|
@@ -106,7 +106,8 @@ Learning more:
|
||||
* Subscribe to the
|
||||
[Zulip announcements email list](https://groups.google.com/forum/#!forum/zulip-announce)
|
||||
for server administrators. This extremely low-traffic list is for
|
||||
important announcements, including new releases and security issues.
|
||||
important announcements, including new releases and security issues. You can also use the
|
||||
[RSS feed](https://groups.google.com/forum/#!aboutgroup/zulip-announce).
|
||||
* Follow [Zulip on Twitter](https://twitter.com/zulip).
|
||||
* Learn how to [configure your Zulip server settings](settings.md).
|
||||
* Learn about [Backups, export and import](../production/export-and-import.md)
|
||||
|
@@ -56,7 +56,6 @@ zulip-workers:zulip-events-confirmation-emails RUNNING pid 21
|
||||
zulip-workers:zulip-events-digest_emails RUNNING pid 2205, uptime 1:13:11
|
||||
zulip-workers:zulip-events-email_mirror RUNNING pid 2203, uptime 1:13:11
|
||||
zulip-workers:zulip-events-error_reports RUNNING pid 2200, uptime 1:13:11
|
||||
zulip-workers:zulip-events-feedback_messages RUNNING pid 2207, uptime 1:13:11
|
||||
zulip-workers:zulip-events-missedmessage_mobile_notifications RUNNING pid 2204, uptime 1:13:11
|
||||
zulip-workers:zulip-events-missedmessage_reminders RUNNING pid 2206, uptime 1:13:11
|
||||
zulip-workers:zulip-events-signups RUNNING pid 2198, uptime 1:13:11
|
||||
|
@@ -6,7 +6,7 @@ There are three disjoint sets of users you care about
|
||||
for typical Zulip realms:
|
||||
|
||||
- active users in your realm
|
||||
- cross-realm users like feedback@zulip.com
|
||||
- cross-realm users like welcome-bot@zulip.com
|
||||
- deactivated users in your realm
|
||||
|
||||
You can also think in terms of these user populations:
|
||||
|
@@ -2,6 +2,8 @@ var util = require("util");
|
||||
|
||||
var test_credentials = require('../../var/casper/test_credentials.js').test_credentials;
|
||||
|
||||
casper.options.clientScripts.push("frontend_tests/casper_lib/polyfill.js");
|
||||
|
||||
function timestamp() {
|
||||
return new Date().getTime();
|
||||
}
|
||||
|
12
frontend_tests/casper_lib/polyfill.js
Normal file
12
frontend_tests/casper_lib/polyfill.js
Normal file
@@ -0,0 +1,12 @@
|
||||
/* eslint-env browser */
|
||||
|
||||
// PhantomJS doesn’t support new DOMParser().parseFromString(…, "text/html").
|
||||
var real_parseFromString = DOMParser.prototype.parseFromString;
|
||||
DOMParser.prototype.parseFromString = function (string, type) {
|
||||
if (type === "text/html") {
|
||||
var doc = document.implementation.createHTMLDocument("");
|
||||
doc.documentElement.innerHTML = string;
|
||||
return doc;
|
||||
}
|
||||
return real_parseFromString.apply(this, arguments);
|
||||
};
|
@@ -143,7 +143,7 @@ casper.then(function () {
|
||||
casper.then(function () {
|
||||
casper.click('*[title="Narrow to your private messages with Cordelia Lear"]');
|
||||
});
|
||||
casper.waitUntilVisible('li[data-user-ids-string="9"].expanded_private_message.active-sub-filter', function () {
|
||||
casper.waitUntilVisible('li[data-user-ids-string="8"].expanded_private_message.active-sub-filter', function () {
|
||||
casper.page.sendEvent('keypress', 'c');
|
||||
});
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
const { JSDOM } = require("jsdom");
|
||||
|
||||
set_global('bridge', false);
|
||||
|
||||
set_global('blueslip', global.make_zblueslip({
|
||||
@@ -7,6 +9,7 @@ set_global('blueslip', global.make_zblueslip({
|
||||
const noop = function () {};
|
||||
|
||||
set_global('$', global.make_zjquery());
|
||||
set_global('DOMParser', new JSDOM().window.DOMParser);
|
||||
set_global('i18n', global.stub_i18n);
|
||||
|
||||
const _navigator = {
|
||||
@@ -263,15 +266,15 @@ run_test('validate', () => {
|
||||
});
|
||||
|
||||
run_test('get_invalid_recipient_emails', () => {
|
||||
const feedback_bot = {
|
||||
email: 'feedback@example.com',
|
||||
const welcome_bot = {
|
||||
email: 'welcome-bot@example.com',
|
||||
user_id: 124,
|
||||
full_name: 'Feedback Bot',
|
||||
full_name: 'Welcome Bot',
|
||||
};
|
||||
page_params.cross_realm_bots = [feedback_bot];
|
||||
page_params.cross_realm_bots = [welcome_bot];
|
||||
page_params.user_id = 30;
|
||||
people.initialize();
|
||||
compose_state.private_message_recipient('feedback@example.com');
|
||||
compose_state.private_message_recipient('welcome-bot@example.com');
|
||||
assert.deepEqual(compose.get_invalid_recipient_emails(), []);
|
||||
});
|
||||
|
||||
|
@@ -7,8 +7,9 @@ set_global('compose_ui', {});
|
||||
|
||||
const { JSDOM } = require("jsdom");
|
||||
const { window } = new JSDOM('<!DOCTYPE html><p>Hello world</p>');
|
||||
const { document } = window;
|
||||
const { DOMParser, document } = window;
|
||||
set_global('$', require('jquery')(window));
|
||||
set_global('DOMParser', DOMParser);
|
||||
set_global('document', document);
|
||||
|
||||
const copy_and_paste = zrequire('copy_and_paste');
|
||||
|
@@ -86,7 +86,8 @@ run_test('basics', () => {
|
||||
assert(!filter.contains_only_private_messages());
|
||||
assert(!filter.allow_use_first_unread_when_narrowing());
|
||||
assert(!filter.can_apply_locally());
|
||||
assert(!filter.is_exactly('stream'));
|
||||
assert(filter.can_bucket_by('stream'));
|
||||
assert(filter.can_bucket_by('stream', 'topic'));
|
||||
|
||||
// If our only stream operator is negated, then for all intents and purposes,
|
||||
// we don't consider ourselves to have a stream operator, because we don't
|
||||
@@ -97,6 +98,7 @@ run_test('basics', () => {
|
||||
filter = new Filter(operators);
|
||||
assert(!filter.contains_only_private_messages());
|
||||
assert(!filter.has_operator('stream'));
|
||||
assert(!filter.can_mark_messages_read());
|
||||
|
||||
// Negated searches are just like positive searches for our purposes, since
|
||||
// the search logic happens on the back end and we need to have can_apply_locally()
|
||||
@@ -108,6 +110,7 @@ run_test('basics', () => {
|
||||
assert(!filter.contains_only_private_messages());
|
||||
assert(filter.has_operator('search'));
|
||||
assert(!filter.can_apply_locally());
|
||||
assert(!filter.can_mark_messages_read());
|
||||
|
||||
// Similar logic applies to negated "has" searches.
|
||||
operators = [
|
||||
@@ -117,6 +120,7 @@ run_test('basics', () => {
|
||||
assert(filter.has_operator('has'));
|
||||
assert(!filter.can_apply_locally());
|
||||
assert(!filter.includes_full_stream_history());
|
||||
assert(!filter.can_mark_messages_read());
|
||||
|
||||
operators = [
|
||||
{operator: 'streams', operand: 'public', negated: true},
|
||||
@@ -124,6 +128,7 @@ run_test('basics', () => {
|
||||
filter = new Filter(operators);
|
||||
assert(!filter.contains_only_private_messages());
|
||||
assert(!filter.has_operator('streams'));
|
||||
assert(!filter.can_mark_messages_read());
|
||||
assert(filter.has_negated_operand('streams', 'public'));
|
||||
assert(!filter.can_apply_locally());
|
||||
|
||||
@@ -133,6 +138,7 @@ run_test('basics', () => {
|
||||
filter = new Filter(operators);
|
||||
assert(!filter.contains_only_private_messages());
|
||||
assert(filter.has_operator('streams'));
|
||||
assert(!filter.can_mark_messages_read());
|
||||
assert(!filter.has_negated_operand('streams', 'public'));
|
||||
assert(!filter.can_apply_locally());
|
||||
assert(filter.includes_full_stream_history());
|
||||
@@ -142,6 +148,16 @@ run_test('basics', () => {
|
||||
];
|
||||
filter = new Filter(operators);
|
||||
assert(filter.contains_only_private_messages());
|
||||
assert(filter.can_mark_messages_read());
|
||||
assert(!filter.has_operator('search'));
|
||||
assert(filter.can_apply_locally());
|
||||
|
||||
operators = [
|
||||
{operator: 'is', operand: 'mentioned'},
|
||||
];
|
||||
filter = new Filter(operators);
|
||||
assert(!filter.contains_only_private_messages());
|
||||
assert(filter.can_mark_messages_read());
|
||||
assert(!filter.has_operator('search'));
|
||||
assert(filter.can_apply_locally());
|
||||
|
||||
@@ -200,7 +216,11 @@ function assert_not_mark_read_with_is_operands(additional_operators_to_test) {
|
||||
|
||||
is_operator = [{ operator: 'is', operand: 'mentioned' }];
|
||||
filter = new Filter(additional_operators_to_test.concat(is_operator));
|
||||
assert(!filter.can_mark_messages_read());
|
||||
if (additional_operators_to_test.length === 0) {
|
||||
assert(filter.can_mark_messages_read());
|
||||
} else {
|
||||
assert(!filter.can_mark_messages_read());
|
||||
}
|
||||
|
||||
is_operator = [{ operator: 'is', operand: 'mentioned', negated: true }];
|
||||
filter = new Filter(additional_operators_to_test.concat(is_operator));
|
||||
@@ -275,11 +295,17 @@ run_test('can_mark_messages_read', () => {
|
||||
{ operator: 'pm-with', operand: 'joe@example.com,' },
|
||||
];
|
||||
|
||||
const pm_with_negated = [
|
||||
{ operator: 'pm-with', operand: 'joe@example.com,', negated: true},
|
||||
];
|
||||
|
||||
const group_pm = [
|
||||
{ operator: 'pm-with', operand: 'joe@example.com,STEVE@foo.com' },
|
||||
];
|
||||
filter = new Filter(pm_with);
|
||||
assert(filter.can_mark_messages_read());
|
||||
filter = new Filter(pm_with_negated);
|
||||
assert(!filter.can_mark_messages_read());
|
||||
filter = new Filter(group_pm);
|
||||
assert(filter.can_mark_messages_read());
|
||||
assert_not_mark_read_with_is_operands(group_pm);
|
||||
@@ -310,11 +336,28 @@ run_test('can_mark_messages_read', () => {
|
||||
const in_home = [
|
||||
{ operator: 'in', operand: 'home' },
|
||||
];
|
||||
const in_home_negated = [
|
||||
{ operator: 'in', operand: 'home', negated: true },
|
||||
];
|
||||
filter = new Filter(in_home);
|
||||
assert(filter.can_mark_messages_read());
|
||||
assert_not_mark_read_with_is_operands(in_home);
|
||||
assert_not_mark_read_with_has_operands(in_home);
|
||||
assert_not_mark_read_when_searching(in_home);
|
||||
filter = new Filter(in_home_negated);
|
||||
assert(!filter.can_mark_messages_read());
|
||||
|
||||
// Do not mark messages as read when in an unsupported 'in:*' filter.
|
||||
const in_random = [
|
||||
{ operator: 'in', operand: 'xxxxxxxxx' },
|
||||
];
|
||||
const in_random_negated = [
|
||||
{ operator: 'in', operand: 'xxxxxxxxx', negated: true },
|
||||
];
|
||||
filter = new Filter(in_random);
|
||||
assert(!filter.can_mark_messages_read());
|
||||
filter = new Filter(in_random_negated);
|
||||
assert(!filter.can_mark_messages_read());
|
||||
});
|
||||
|
||||
run_test('show_first_unread', () => {
|
||||
@@ -369,7 +412,7 @@ run_test('new_style_operators', () => {
|
||||
const filter = new Filter(operators);
|
||||
|
||||
assert.deepEqual(filter.operands('stream'), ['foo']);
|
||||
assert(filter.is_exactly('stream'));
|
||||
assert(filter.can_bucket_by('stream'));
|
||||
});
|
||||
|
||||
run_test('public_operators', () => {
|
||||
@@ -381,7 +424,7 @@ run_test('public_operators', () => {
|
||||
|
||||
let filter = new Filter(operators);
|
||||
assert_same_operators(filter.public_operators(), operators);
|
||||
assert(!filter.is_exactly('stream'));
|
||||
assert(filter.can_bucket_by('stream'));
|
||||
|
||||
global.page_params.narrow_stream = 'default';
|
||||
operators = [
|
||||
@@ -391,6 +434,28 @@ run_test('public_operators', () => {
|
||||
assert_same_operators(filter.public_operators(), []);
|
||||
});
|
||||
|
||||
run_test('redundancies', () => {
|
||||
let terms;
|
||||
let filter;
|
||||
|
||||
terms = [
|
||||
{ operator: 'pm-with', operand: 'joe@example.com,' },
|
||||
{ operator: 'is', operand: 'private' },
|
||||
];
|
||||
filter = new Filter(terms);
|
||||
assert(filter.can_bucket_by('pm-with'));
|
||||
|
||||
terms = [
|
||||
{ operator: 'pm-with',
|
||||
operand: 'joe@example.com,',
|
||||
negated: true,
|
||||
},
|
||||
{ operator: 'is', operand: 'private' },
|
||||
];
|
||||
filter = new Filter(terms);
|
||||
assert(filter.can_bucket_by('is-private', 'not-pm-with'));
|
||||
});
|
||||
|
||||
run_test('canonicalizations', () => {
|
||||
assert.equal(Filter.canonicalize_operator('Is'), 'is');
|
||||
assert.equal(Filter.canonicalize_operator('Stream'), 'stream');
|
||||
@@ -980,14 +1045,14 @@ run_test('describe', () => {
|
||||
assert.equal(Filter.describe(narrow), string);
|
||||
});
|
||||
|
||||
run_test('is_functions', () => {
|
||||
run_test('can_bucket_by', () => {
|
||||
let terms = [
|
||||
{operator: 'stream', operand: 'My Stream'},
|
||||
];
|
||||
let filter = new Filter(terms);
|
||||
assert.equal(filter.is_exactly('stream'), true);
|
||||
assert.equal(filter.is_exactly('stream', 'topic'), false);
|
||||
assert.equal(filter.is_exactly('pm-with'), false);
|
||||
assert.equal(filter.can_bucket_by('stream'), true);
|
||||
assert.equal(filter.can_bucket_by('stream', 'topic'), false);
|
||||
assert.equal(filter.can_bucket_by('pm-with'), false);
|
||||
|
||||
terms = [
|
||||
// try a non-orthodox ordering
|
||||
@@ -997,9 +1062,6 @@ run_test('is_functions', () => {
|
||||
filter = new Filter(terms);
|
||||
assert.equal(filter.can_bucket_by('stream'), true);
|
||||
assert.equal(filter.can_bucket_by('stream', 'topic'), true);
|
||||
assert.equal(filter.is_exactly('stream'), false);
|
||||
assert.equal(filter.is_exactly('stream', 'topic'), true);
|
||||
assert.equal(filter.is_exactly('pm-with'), false);
|
||||
assert.equal(filter.can_bucket_by('pm-with'), false);
|
||||
|
||||
terms = [
|
||||
@@ -1009,49 +1071,45 @@ run_test('is_functions', () => {
|
||||
filter = new Filter(terms);
|
||||
assert.equal(filter.can_bucket_by('stream'), false);
|
||||
assert.equal(filter.can_bucket_by('stream', 'topic'), false);
|
||||
assert.equal(filter.is_exactly('stream'), false);
|
||||
assert.equal(filter.is_exactly('stream', 'topic'), false);
|
||||
assert.equal(filter.is_exactly('pm-with'), false);
|
||||
assert.equal(filter.can_bucket_by('pm-with'), false);
|
||||
|
||||
terms = [
|
||||
{operator: 'pm-with', operand: 'foo@example.com', negated: true},
|
||||
];
|
||||
filter = new Filter(terms);
|
||||
assert.equal(filter.is_exactly('stream'), false);
|
||||
assert.equal(filter.is_exactly('stream', 'topic'), false);
|
||||
assert.equal(filter.is_exactly('pm-with'), false);
|
||||
assert.equal(filter.can_bucket_by('stream'), false);
|
||||
assert.equal(filter.can_bucket_by('stream', 'topic'), false);
|
||||
assert.equal(filter.can_bucket_by('pm-with'), false);
|
||||
|
||||
terms = [
|
||||
{operator: 'pm-with', operand: 'foo@example.com,bar@example.com'},
|
||||
];
|
||||
filter = new Filter(terms);
|
||||
assert.equal(filter.is_exactly('stream'), false);
|
||||
assert.equal(filter.is_exactly('stream', 'topic'), false);
|
||||
assert.equal(filter.is_exactly('pm-with'), true);
|
||||
assert.equal(filter.is_exactly('is-mentioned'), false);
|
||||
assert.equal(filter.is_exactly('is-private'), false);
|
||||
assert.equal(filter.can_bucket_by('stream'), false);
|
||||
assert.equal(filter.can_bucket_by('stream', 'topic'), false);
|
||||
assert.equal(filter.can_bucket_by('pm-with'), true);
|
||||
assert.equal(filter.can_bucket_by('is-mentioned'), false);
|
||||
assert.equal(filter.can_bucket_by('is-private'), false);
|
||||
|
||||
terms = [
|
||||
{operator: 'is', operand: 'private'},
|
||||
];
|
||||
filter = new Filter(terms);
|
||||
assert.equal(filter.is_exactly('is-mentioned'), false);
|
||||
assert.equal(filter.is_exactly('is-private'), true);
|
||||
assert.equal(filter.can_bucket_by('is-mentioned'), false);
|
||||
assert.equal(filter.can_bucket_by('is-private'), true);
|
||||
|
||||
terms = [
|
||||
{operator: 'is', operand: 'mentioned'},
|
||||
];
|
||||
filter = new Filter(terms);
|
||||
assert.equal(filter.is_exactly('is-mentioned'), true);
|
||||
assert.equal(filter.is_exactly('is-private'), false);
|
||||
assert.equal(filter.can_bucket_by('is-mentioned'), true);
|
||||
assert.equal(filter.can_bucket_by('is-private'), false);
|
||||
|
||||
terms = [
|
||||
{operator: 'is', operand: 'mentioned'},
|
||||
{operator: 'is', operand: 'starred'},
|
||||
];
|
||||
filter = new Filter(terms);
|
||||
assert.equal(filter.is_exactly('is-mentioned'), false);
|
||||
assert.equal(filter.is_exactly('is-private'), false);
|
||||
assert.equal(filter.can_bucket_by('is-mentioned'), true);
|
||||
assert.equal(filter.can_bucket_by('is-private'), false);
|
||||
|
||||
@@ -1064,8 +1122,8 @@ run_test('is_functions', () => {
|
||||
{operator: 'is', operand: 'mentioned', negated: true},
|
||||
];
|
||||
filter = new Filter(terms);
|
||||
assert.equal(filter.is_exactly('is-mentioned'), false);
|
||||
assert.equal(filter.is_exactly('is-private'), false);
|
||||
assert.equal(filter.can_bucket_by('is-mentioned'), false);
|
||||
assert.equal(filter.can_bucket_by('is-private'), false);
|
||||
});
|
||||
|
||||
run_test('term_type', () => {
|
||||
@@ -1081,7 +1139,7 @@ run_test('term_type', () => {
|
||||
};
|
||||
}
|
||||
|
||||
assert_term_type(term('streams', 'public'), 'streams');
|
||||
assert_term_type(term('streams', 'public'), 'streams-public');
|
||||
assert_term_type(term('stream', 'whatever'), 'stream');
|
||||
assert_term_type(term('pm-with', 'whomever'), 'pm-with');
|
||||
assert_term_type(term('pm-with', 'whomever', true), 'not-pm-with');
|
||||
|
@@ -30,7 +30,6 @@ run_test('stream', () => {
|
||||
['search', 'yo'],
|
||||
]);
|
||||
assert(narrow_state.active());
|
||||
assert(!narrow_state.is_reading_mode());
|
||||
|
||||
assert.equal(narrow_state.stream(), 'Test');
|
||||
assert.equal(narrow_state.stream_id(), test_stream.stream_id);
|
||||
@@ -59,7 +58,6 @@ run_test('narrowed', () => {
|
||||
assert(!narrow_state.narrowed_to_topic());
|
||||
assert(!narrow_state.narrowed_by_stream_reply());
|
||||
assert.equal(narrow_state.stream_id(), undefined);
|
||||
assert(narrow_state.is_reading_mode());
|
||||
|
||||
set_filter([['stream', 'Foo']]);
|
||||
assert(!narrow_state.narrowed_to_pms());
|
||||
@@ -69,7 +67,6 @@ run_test('narrowed', () => {
|
||||
assert(!narrow_state.narrowed_to_search());
|
||||
assert(!narrow_state.narrowed_to_topic());
|
||||
assert(narrow_state.narrowed_by_stream_reply());
|
||||
assert(narrow_state.is_reading_mode());
|
||||
|
||||
set_filter([['pm-with', 'steve@zulip.com']]);
|
||||
assert(narrow_state.narrowed_to_pms());
|
||||
@@ -79,7 +76,6 @@ run_test('narrowed', () => {
|
||||
assert(!narrow_state.narrowed_to_search());
|
||||
assert(!narrow_state.narrowed_to_topic());
|
||||
assert(!narrow_state.narrowed_by_stream_reply());
|
||||
assert(narrow_state.is_reading_mode());
|
||||
|
||||
set_filter([['stream', 'Foo'], ['topic', 'bar']]);
|
||||
assert(!narrow_state.narrowed_to_pms());
|
||||
@@ -89,7 +85,6 @@ run_test('narrowed', () => {
|
||||
assert(!narrow_state.narrowed_to_search());
|
||||
assert(narrow_state.narrowed_to_topic());
|
||||
assert(!narrow_state.narrowed_by_stream_reply());
|
||||
assert(narrow_state.is_reading_mode());
|
||||
|
||||
set_filter([['search', 'grail']]);
|
||||
assert(!narrow_state.narrowed_to_pms());
|
||||
@@ -99,7 +94,6 @@ run_test('narrowed', () => {
|
||||
assert(narrow_state.narrowed_to_search());
|
||||
assert(!narrow_state.narrowed_to_topic());
|
||||
assert(!narrow_state.narrowed_by_stream_reply());
|
||||
assert(!narrow_state.is_reading_mode());
|
||||
});
|
||||
|
||||
run_test('operators', () => {
|
||||
|
@@ -18,7 +18,7 @@ zrequire("people");
|
||||
run_test('insert_recent_private_message', () => {
|
||||
set_global('page_params', {
|
||||
recent_private_conversations: [
|
||||
{user_ids: [1, 2],
|
||||
{user_ids: [11, 2],
|
||||
max_message_id: 150,
|
||||
},
|
||||
{user_ids: [1],
|
||||
@@ -33,24 +33,24 @@ run_test('insert_recent_private_message', () => {
|
||||
pmc.recent.initialize();
|
||||
|
||||
assert.deepEqual(pmc.recent.get(), [
|
||||
{user_ids_string: '1,2', max_message_id: 150},
|
||||
{user_ids_string: '2,11', max_message_id: 150},
|
||||
{user_ids_string: '1', max_message_id: 111},
|
||||
{user_ids_string: '15', max_message_id: 7},
|
||||
]);
|
||||
|
||||
pmc.recent.insert('1', 1001);
|
||||
pmc.recent.insert('2', 2001);
|
||||
pmc.recent.insert('1', 3001);
|
||||
pmc.recent.insert([1], 1001);
|
||||
pmc.recent.insert([2], 2001);
|
||||
pmc.recent.insert([1], 3001);
|
||||
|
||||
// try to backdate user1's latest message
|
||||
pmc.recent.insert('1', 555);
|
||||
pmc.recent.insert([1], 555);
|
||||
|
||||
assert.deepEqual(pmc.recent.get(), [
|
||||
{user_ids_string: '1', max_message_id: 3001},
|
||||
{user_ids_string: '2', max_message_id: 2001},
|
||||
{user_ids_string: '1,2', max_message_id: 150},
|
||||
{user_ids_string: '2,11', max_message_id: 150},
|
||||
{user_ids_string: '15', max_message_id: 7},
|
||||
]);
|
||||
|
||||
assert.deepEqual(pmc.recent.get_strings(), ['1', '2', '1,2', '15']);
|
||||
assert.deepEqual(pmc.recent.get_strings(), ['1', '2', '2,11', '15']);
|
||||
});
|
||||
|
@@ -73,9 +73,8 @@ run_test('build_private_messages_list', () => {
|
||||
const active_conversation_2 = 'me@zulip.com,alice@zulip.com';
|
||||
let max_conversations = 5;
|
||||
|
||||
const user_ids_string = '101,102';
|
||||
const timestamp = 0;
|
||||
pm_conversations.recent.insert(user_ids_string, timestamp);
|
||||
pm_conversations.recent.insert([101, 102], timestamp);
|
||||
|
||||
global.unread.num_unread_for_person = function () {
|
||||
return 1;
|
||||
@@ -125,9 +124,8 @@ run_test('build_private_messages_list_bot', () => {
|
||||
const active_conversation_1 = 'outgoingwebhook@zulip.com';
|
||||
const max_conversations = 5;
|
||||
|
||||
const user_ids_string = '314';
|
||||
const timestamp = 0;
|
||||
pm_conversations.recent.insert(user_ids_string, timestamp);
|
||||
pm_conversations.recent.insert([314], timestamp);
|
||||
|
||||
global.unread.num_unread_for_person = function () {
|
||||
return 1;
|
||||
|
@@ -41,13 +41,10 @@ run_test('generate_zuliprc_uri', () => {
|
||||
});
|
||||
|
||||
run_test('generate_zuliprc_content', () => {
|
||||
const user = {
|
||||
email: "admin12@chatting.net",
|
||||
api_key: "nSlA0mUm7G42LP85lMv7syqFTzDE2q34",
|
||||
};
|
||||
const content = settings_bots.generate_zuliprc_content(user.email, user.api_key);
|
||||
const expected = "[api]\nemail=admin12@chatting.net\n" +
|
||||
"key=nSlA0mUm7G42LP85lMv7syqFTzDE2q34\n" +
|
||||
const bot_user = bot_data.get(1);
|
||||
const content = settings_bots.generate_zuliprc_content(bot_user);
|
||||
const expected = "[api]\nemail=error-bot@zulip.org\n" +
|
||||
"key=QadL788EkiottHmukyhHgePUFHREiu8b\n" +
|
||||
"site=https://chat.example.com\n";
|
||||
|
||||
assert.equal(content, expected);
|
||||
|
@@ -9,6 +9,7 @@ zrequire('stream_edit');
|
||||
const { JSDOM } = require("jsdom");
|
||||
const { window } = new JSDOM();
|
||||
global.$ = require('jquery')(window);
|
||||
set_global('DOMParser', window.DOMParser);
|
||||
|
||||
// When writing these tests, the following command might be helpful:
|
||||
// ./tools/get-handlebar-vars static/templates/*.hbs
|
||||
@@ -1450,7 +1451,7 @@ run_test('typing_notifications', () => {
|
||||
html += '</ul>';
|
||||
|
||||
const li = $(html).find('li').first();
|
||||
assert.equal(li.text(), 'Hamlet is typing...');
|
||||
assert.equal(li.text(), 'translated: Hamlet is typing...');
|
||||
|
||||
});
|
||||
|
||||
|
@@ -1,4 +1,7 @@
|
||||
const { JSDOM } = require("jsdom");
|
||||
|
||||
set_global('$', global.make_zjquery());
|
||||
set_global('DOMParser', new JSDOM().window.DOMParser);
|
||||
set_global('blueslip', global.make_zblueslip({}));
|
||||
set_global('document', {});
|
||||
|
||||
@@ -301,3 +304,19 @@ run_test('move_array_elements_to_front', () => {
|
||||
assert(emails_actual[i] === emails_expected[i]);
|
||||
}
|
||||
});
|
||||
|
||||
run_test("clean_user_content_links", () => {
|
||||
window.location.href = "http://zulip.zulipdev.com/";
|
||||
assert.equal(
|
||||
util.clean_user_content_links(
|
||||
'<a href="http://example.com">good</a> ' +
|
||||
'<a href="http://localhost:NNNN">invalid</a> ' +
|
||||
'<a href="javascript:alert(1)">unsafe</a> ' +
|
||||
'<a href="/#fragment" target="_blank">fragment</a>'
|
||||
),
|
||||
'<a href="http://example.com" target="_blank" rel="noopener noreferrer">good</a> ' +
|
||||
'<a>invalid</a> ' +
|
||||
'<a>unsafe</a> ' +
|
||||
'<a href="/#fragment">fragment</a>'
|
||||
);
|
||||
});
|
||||
|
@@ -7,6 +7,7 @@
|
||||
# Ansgar Hofmann <ansgar@trollgames.de>, 2016
|
||||
# Ansgar Hofmann <ansgar@trollgames.de>, 2017,2019
|
||||
# n3w2oo <kontakt@n3w2oo.net>, 2016
|
||||
# Christian Spaan, 2019
|
||||
# Daniel W. <daniel.wos.86@googlemail.com>, 2015
|
||||
# Dante Cassius <exaltedunmelody@gmail.com>, 2016
|
||||
# Dennis Stengele <dennis@stengele.me>, 2019
|
||||
@@ -22,6 +23,7 @@
|
||||
# BornToBeRoot, 2015
|
||||
# n3w2oo <kontakt@n3w2oo.net>, 2016
|
||||
# Niklas P <niklaspotthoff@gmail.com>, 2016
|
||||
# Philipp Mysz <philipp_mysz@gmx.de>, 2019
|
||||
# Philip Steffan <philip.steffan@okfn.de>, 2019
|
||||
# Robert Hönig <robhoenig@gmail.com>, 2017-2019
|
||||
# Robin Richtsfeld <robin.richtsfeld@gmail.com>, 2018
|
||||
@@ -32,8 +34,8 @@ msgstr ""
|
||||
"Project-Id-Version: Zulip\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-12-13 03:00+0000\n"
|
||||
"PO-Revision-Date: 2019-12-13 03:01+0000\n"
|
||||
"Last-Translator: Tim Abbott <tabbott@kandralabs.com>\n"
|
||||
"PO-Revision-Date: 2019-12-14 23:42+0000\n"
|
||||
"Last-Translator: Christian Spaan\n"
|
||||
"Language-Team: German (http://www.transifex.com/zulip/zulip/language/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -499,7 +501,7 @@ msgid ""
|
||||
" target=\"_blank\">personal history</a>.\n"
|
||||
" Consider <a class=\"search-shared-history\" href=\"\">searching all public streams</a>.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
msgstr "\n Keine weiteren Ergebnisse in Ihrer\n <a href=\"/help/search-for-messages#searching-shared-history\"\n target=\"_blank\">persönlichen Chronik</a>.\n Versuchen Sie, <a class=\"search-shared-history\" href=\"\">alle öffentlichen Streams zu durchsuchen</a>.\n "
|
||||
|
||||
#: templates/zerver/app/home.html:35
|
||||
msgid "Welcome to Zulip."
|
||||
@@ -3776,7 +3778,7 @@ msgstr "%s ist kein Integer"
|
||||
#: zerver/lib/validator.py:101
|
||||
#, python-format
|
||||
msgid "Invalid %s"
|
||||
msgstr ""
|
||||
msgstr "Ungültig %s"
|
||||
|
||||
#: zerver/lib/validator.py:108
|
||||
#, python-format
|
||||
|
@@ -10,9 +10,9 @@
|
||||
"1 hour": "1 Stunde",
|
||||
"1 week": "1 Woche",
|
||||
"10 minutes": "10 Minuten",
|
||||
"12-hour clock (5:00 PM)": "",
|
||||
"12-hour clock (5:00 PM)": "12-Stunden Format (5:00 PM)",
|
||||
"2 minutes": "2 Minuten",
|
||||
"24-hour clock (17:00)": "",
|
||||
"24-hour clock (17:00)": "24-Stunden Format (17:00)",
|
||||
"3 days": "3 Tage",
|
||||
"<a href=\"/help/export-your-organization\" target=\"_blank\">Click here</a> to learn about exporting private streams and messages.": "<a href=\"/help/export-your-organization\" target=\"_blank\">Hier klicken</a>, um zu erfahren, wie man private Streams und Nachrichten exportiert.",
|
||||
"<a href=\"/upgrade\" target=\"_blank\">Upgrade</a> for more space.": "<a href=\"/upgrade\" target=\"_blank\">Upgrade</a> für mehr Platz.",
|
||||
@@ -312,7 +312,7 @@
|
||||
"Language settings": "Spracheinstellungen",
|
||||
"Large number of subscribers": "Hohe Anzahl an Mitgliedern",
|
||||
"Last active": "Zuletzt aktiv",
|
||||
"Last active: __last_seen__": "",
|
||||
"Last active: __last_seen__": "Zuletzt aktiv: __last_seen__",
|
||||
"Last modified": "Zuletzt geändert",
|
||||
"Local time": "Lokale Zeit",
|
||||
"Looking for our <a href=\"/integrations\" target=\"_blank\">Integrations</a> or <a href=\"/api\" target=\"_blank\">API</a> documentation?": "Suchst Du unsere Dokumentation für <a href=\"/integrations\" target=\"_blank\">Integrationen</a> oder die <a href=\"/api\" target=\"_blank\">API</a>?",
|
||||
@@ -384,7 +384,7 @@
|
||||
"No invites match your current filter.": "Keine Einladung passt zu diesem Filter.",
|
||||
"No linkifiers set.": "Keine Linkifiers gesetzt.",
|
||||
"No more topics.": "Keine weiteren Themen.",
|
||||
"No owner": "",
|
||||
"No owner": "Kein Besitzer",
|
||||
"No restrictions": "Keine Beschränkungen",
|
||||
"No users match your current filter.": "Dein aktueller Filter stimmt mit keinem Nutzer überein.",
|
||||
"None": "Nichts",
|
||||
@@ -393,7 +393,7 @@
|
||||
"Nothing to preview": "Keine Vorschau vorhanden",
|
||||
"Notification sound": "Benachrichtigungston",
|
||||
"Notifications": "Benachrichtigungen",
|
||||
"Notifications for @all/@everyone mentions": "",
|
||||
"Notifications for @all/@everyone mentions": "Benachrichtigungen für @all/@everyone Erwähnungen",
|
||||
"Notifications stream changed!": "Stream für Benachrichtigungen wurde geändert!",
|
||||
"Notifications stream disabled!": "Stream für Benachrichtungen wurde deaktiviert!",
|
||||
"Offline": "Offline",
|
||||
@@ -403,7 +403,7 @@
|
||||
"Only organization administrators can add custom emoji in this organization.": "In dieser Organisation können nur Administratoren eigene\nEmojis hinzufügen.",
|
||||
"Only organization administrators can add generic bots": "Nur Administratoren dieser Organisation können Standard-Bots hinzufügen",
|
||||
"Only organization administrators can edit these settings.": "Diese Einstellungen können nur von Administratoren dieser Organisation bearbeitet werden.",
|
||||
"Only organization administrators can modify user groups in this organization.": "",
|
||||
"Only organization administrators can modify user groups in this organization.": "Nur Organisationsadministratoren können Nutzergruppen in dieser Organisation ändern.",
|
||||
"Only organization administrators can post.": "Nur Administratoren dieser Organisation können posten.",
|
||||
"Only organization admins are allowed to post to this stream.": "Nur Administratoren dieser Organisation können in diesen Stream schreiben.",
|
||||
"Only stream members can add users to a private stream": "Nur Stream-Mitglieder können Benutzer zu einem privaten Stream hinzufügen",
|
||||
@@ -424,7 +424,7 @@
|
||||
"Other settings": "Andere Einstellung",
|
||||
"Outgoing webhook message format": "Outgoing Webhook Nachrichtenformat",
|
||||
"Owner": "Besitzer",
|
||||
"Owner: __name__": "",
|
||||
"Owner: __name__": "Besitzer: __name__",
|
||||
"Password": "Passwort",
|
||||
"Password is too weak": "Zu schwaches Passwort",
|
||||
"Password should be at least __length__ characters long": "Passwort sollte mindestens __length__ Zeichen enthalten",
|
||||
@@ -477,7 +477,7 @@
|
||||
"Revoke invitation to __email__": "Einladung an __email__ widerrufen",
|
||||
"Revoke now": "Jetzt widerrufen",
|
||||
"Role": "Rolle",
|
||||
"SAVING": "",
|
||||
"SAVING": "SPEICHERN",
|
||||
"Saturday": "Samstag",
|
||||
"Save": "Speichern",
|
||||
"Save changes": "Änderungen speichern",
|
||||
@@ -566,7 +566,7 @@
|
||||
"This stream is reserved for <strong>announcements</strong>. <br /> Are you sure you want to message all <strong>__count__</strong> people in this stream?": "Dieser Stream ist auschließlich für <strong>Ankündigungen</strong>. <br />Bist Du sicher, dass Du eine Nachricht an alle <strong>__count__</strong> Nutzer in diesem Stream versenden möchtest?",
|
||||
"Thursday": "Donnerstag",
|
||||
"Time": "Zeit",
|
||||
"Time format": "",
|
||||
"Time format": "Zeitformat",
|
||||
"Time settings": "Zeiteinstellungen",
|
||||
"Time zone": "Zeitzone",
|
||||
"Time's up!": "Zeit vorbei!",
|
||||
@@ -585,7 +585,7 @@
|
||||
"URL pattern": "URL-Muster",
|
||||
"Un-collapse": "Aufklappen",
|
||||
"Unable to upload that many files at once.": "Zu viele Dateien, um sie auf einmal hochzuladen.",
|
||||
"Unavailable": "",
|
||||
"Unavailable": "Nicht verfügbar",
|
||||
"Uncheck all": "Alles abwählen",
|
||||
"Unknown": "Unbekannt",
|
||||
"Unless I say otherwise for a particular stream, I want:": "Wenn Du es nicht anders einstellst, erhältst Du:",
|
||||
@@ -642,11 +642,11 @@
|
||||
"We are about to have a poll. Please wait for the question.": "Wir haben gleich eine Umfrage. Bitte warte auf die Frage.",
|
||||
"We recommend against deleting topics unless needed for security reasons or managing abuse. Deleted messages can be confusing for users who may later visit the topic via notifications.": "Wir empfehlen, keine Themen zu löschen, es sei denn aus Sicherheitsgründen oder um Missbrauch einzudämmen. Gelöschte Nachrichten können verwirrend für andere Nutzer sein, wenn sie versuchen, das Thema über eine Benachrichtigung zu öffnen.",
|
||||
"Wednesday": "Mittwoch",
|
||||
"Who can access user email addresses": "",
|
||||
"Who can access user email addresses": "Kann auf Nutzer-Email-Adressen zugreifen",
|
||||
"Who can add bots": "Wer kann Bots hinzufügen",
|
||||
"Who can add custom emoji": "Wer kann eigene Emojis hinzufügen",
|
||||
"Who can add users to streams": "Wer kann Nutzer zu Streams hinzufügen",
|
||||
"Who can create and manage user groups": "",
|
||||
"Who can create and manage user groups": "Kann Nutzergruppen erstellen und verwalten",
|
||||
"Who can create streams": "Wer kann Streams erstellen",
|
||||
"Working\u2026": "Arbeitet…",
|
||||
"Would you like to unstar all starred messages? This action cannot be undone.": "Möchtest du alle markierten Nachrichten aufheben? Dieser Vorgang kann nicht rückgängig gemacht werden.",
|
||||
|
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-12-13 04:30+0000\n"
|
||||
"POT-Creation-Date: 2020-01-16 19:35+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -18,7 +18,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: analytics/views.py:80 zerver/decorator.py:522 zerver/decorator.py:531
|
||||
#: analytics/views.py:80 zerver/decorator.py:525 zerver/decorator.py:534
|
||||
msgid "Not allowed for guest users"
|
||||
msgstr ""
|
||||
|
||||
@@ -1335,7 +1335,7 @@ msgstr ""
|
||||
msgid "Organization permissions"
|
||||
msgstr ""
|
||||
|
||||
#: templates/zerver/app/settings_overlay.html:72 zerver/models.py:1769
|
||||
#: templates/zerver/app/settings_overlay.html:72 zerver/models.py:1767
|
||||
msgid "Custom emoji"
|
||||
msgstr ""
|
||||
|
||||
@@ -2925,35 +2925,35 @@ msgstr ""
|
||||
msgid "Account is not associated with this subdomain"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:471 zerver/decorator.py:533
|
||||
#: zerver/decorator.py:474 zerver/decorator.py:536
|
||||
msgid "This endpoint does not accept bot requests."
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:513
|
||||
#: zerver/decorator.py:516
|
||||
msgid "Must be an server administrator"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:588
|
||||
#: zerver/decorator.py:591
|
||||
msgid "This endpoint requires HTTP basic authentication."
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:591
|
||||
#: zerver/decorator.py:594
|
||||
msgid "Invalid authorization header for basic auth"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:593
|
||||
#: zerver/decorator.py:596
|
||||
msgid "Missing authorization header for basic auth"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:670
|
||||
#: zerver/decorator.py:673
|
||||
msgid "Not logged in"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:681
|
||||
#: zerver/decorator.py:684
|
||||
msgid "Webhook bots can only access webhooks"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:740
|
||||
#: zerver/decorator.py:743
|
||||
msgid "Access denied"
|
||||
msgstr ""
|
||||
|
||||
@@ -3376,7 +3376,7 @@ msgstr ""
|
||||
msgid "Must be an organization administrator or emoji author"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/lib/emoji.py:109 zerver/models.py:601
|
||||
#: zerver/lib/emoji.py:109 zerver/models.py:599
|
||||
msgid "Invalid characters in emoji name"
|
||||
msgstr ""
|
||||
|
||||
@@ -3428,7 +3428,11 @@ msgstr ""
|
||||
msgid "Invalid API key"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/lib/exceptions.py:229
|
||||
#: zerver/lib/exceptions.py:222
|
||||
msgid "Malformed API key"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/lib/exceptions.py:234
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"The '{event_type}' event isn't currently supported by the {webhook_name} "
|
||||
@@ -3870,71 +3874,71 @@ msgstr ""
|
||||
msgid "API usage exceeded rate limit"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:255
|
||||
#: zerver/models.py:253
|
||||
msgid "stream events"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:274
|
||||
#: zerver/models.py:272
|
||||
msgid "Available on Zulip Standard. Upgrade to access."
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:664
|
||||
#: zerver/models.py:662
|
||||
#, python-format
|
||||
msgid "Invalid filter pattern. Valid characters are %s."
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:680
|
||||
#: zerver/models.py:678
|
||||
msgid "Invalid URL format string."
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:1768
|
||||
#: zerver/models.py:1766
|
||||
msgid "Unicode emoji"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:1770
|
||||
#: zerver/models.py:1768
|
||||
msgid "Zulip extra emoji"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2676
|
||||
#: zerver/models.py:2690
|
||||
#, python-format
|
||||
msgid "Invalid user ID: %d"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2680
|
||||
#: zerver/models.py:2694
|
||||
#, python-format
|
||||
msgid "User with ID %d is deactivated"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2683
|
||||
#: zerver/models.py:2697
|
||||
#, python-format
|
||||
msgid "User with ID %d is a bot"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2713
|
||||
#: zerver/models.py:2727
|
||||
msgid "List of options"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2716
|
||||
#: zerver/models.py:2730
|
||||
msgid "Person picker"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2728
|
||||
#: zerver/models.py:2742
|
||||
msgid "Short text"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2729
|
||||
#: zerver/models.py:2743
|
||||
msgid "Long text"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2730
|
||||
#: zerver/models.py:2744
|
||||
msgid "Date picker"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2731
|
||||
#: zerver/models.py:2745
|
||||
msgid "Link"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2732
|
||||
#: zerver/models.py:2746
|
||||
msgid "External account"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -4,17 +4,17 @@
|
||||
#
|
||||
# Translators:
|
||||
# Alessio Schreiber <alessio.schreiber@gmail.com>, 2019
|
||||
# Andrea <andrea.soccal@elmospa.com>, 2018-2019
|
||||
# Andrea <andrea.soccal@elmospa.com>, 2018-2020
|
||||
# Biagio Laquale <infingerprinting@gmail.com>, 2019
|
||||
# Marcello Nuccio <marcello.nuccio@nuccioservizi.it>, 2019
|
||||
# 8d821b430c6446b8644f76c07c39dc73, 2019
|
||||
# Paolo Midali <pmidali@latek.it>, 2018-2019
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Zulip\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-12-13 03:00+0000\n"
|
||||
"PO-Revision-Date: 2019-12-13 03:01+0000\n"
|
||||
"Last-Translator: Tim Abbott <tabbott@kandralabs.com>\n"
|
||||
"PO-Revision-Date: 2020-01-15 15:02+0000\n"
|
||||
"Last-Translator: Andrea <andrea.soccal@elmospa.com>\n"
|
||||
"Language-Team: Italian (http://www.transifex.com/zulip/zulip/language/it/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -334,7 +334,7 @@ msgstr "Si, Grazie!"
|
||||
|
||||
#: templates/zerver/app/bankruptcy.html:15
|
||||
msgid "No, I'll catch up."
|
||||
msgstr "No, mi raggiungo."
|
||||
msgstr "No grazie, me li guardo tutti."
|
||||
|
||||
#: templates/zerver/app/compose.html:12 templates/zerver/app/compose.html:13
|
||||
#: templates/zerver/app/compose.html:108
|
||||
@@ -800,11 +800,11 @@ msgstr "Limitando"
|
||||
|
||||
#: templates/zerver/app/keyboard_shortcuts.html:155
|
||||
msgid "Narrow to stream"
|
||||
msgstr ""
|
||||
msgstr "Limita al canale"
|
||||
|
||||
#: templates/zerver/app/keyboard_shortcuts.html:159
|
||||
msgid "Narrow to topic or PM conversation"
|
||||
msgstr ""
|
||||
msgstr "Limita al canale o alle conversazioni dei messaggi privati"
|
||||
|
||||
#: templates/zerver/app/keyboard_shortcuts.html:163
|
||||
msgid "Narrow to all private messages"
|
||||
@@ -1198,7 +1198,7 @@ msgstr "Limita per ID di messaggio"
|
||||
|
||||
#: templates/zerver/app/search_operators.html:45
|
||||
msgid "Search all public streams in the organization."
|
||||
msgstr ""
|
||||
msgstr "Cerca in tutti i canali dell'organizzazione."
|
||||
|
||||
#: templates/zerver/app/search_operators.html:49
|
||||
msgid "Narrow to messages with alert words."
|
||||
@@ -1368,11 +1368,11 @@ msgstr "Inviti"
|
||||
|
||||
#: templates/zerver/app/settings_overlay.html:150
|
||||
msgid "Data exports"
|
||||
msgstr ""
|
||||
msgstr "Esportazioni di dati"
|
||||
|
||||
#: templates/zerver/app/settings_overlay.html:156
|
||||
msgid "Show more"
|
||||
msgstr ""
|
||||
msgstr "Mostra di più"
|
||||
|
||||
#: templates/zerver/config_error.html:14
|
||||
msgid ""
|
||||
@@ -1517,7 +1517,7 @@ msgstr "Se non hai richiesto questo cambiamento, ti preghiamo di contattarci imm
|
||||
#: templates/zerver/emails/invitation.source.html:4
|
||||
#: templates/zerver/emails/realm_reactivation.source.html:4
|
||||
msgid "Turtle with envelope"
|
||||
msgstr ""
|
||||
msgstr "Tartaruga con busta"
|
||||
|
||||
#: templates/zerver/emails/compiled/confirm_registration.html:10
|
||||
#: templates/zerver/emails/confirm_registration.source.html:9
|
||||
@@ -1552,7 +1552,7 @@ msgstr "Contattaci in qualsiasi momento a<a href=\"mailto:%(support_email)s\" st
|
||||
#: templates/zerver/emails/compiled/email_base_default.html:87
|
||||
#: templates/zerver/emails/email_base_default.source.html:30
|
||||
msgid "Swimming fish"
|
||||
msgstr ""
|
||||
msgstr "Pesce che nuota"
|
||||
|
||||
#: templates/zerver/emails/compiled/find_team.html:11
|
||||
#: templates/zerver/emails/find_team.source.html:10
|
||||
@@ -1687,7 +1687,7 @@ msgstr "PS: Seguici su <a href=\"https://twitter.com/zulip\" style=\"color:#46aa
|
||||
#: templates/zerver/emails/compiled/followup_day2.html:4
|
||||
#: templates/zerver/emails/followup_day2.source.html:4
|
||||
msgid "Octopus box with heart"
|
||||
msgstr ""
|
||||
msgstr "Scatola di polpo con cuore"
|
||||
|
||||
#: templates/zerver/emails/compiled/followup_day2.html:9
|
||||
#: templates/zerver/emails/followup_day2.source.html:8
|
||||
@@ -1706,7 +1706,7 @@ msgstr "Volevo condividere un'ultima cosa con te: alcuni suggerimenti sugli argo
|
||||
#: templates/zerver/emails/compiled/followup_day2.html:13
|
||||
#: templates/zerver/emails/followup_day2.source.html:12
|
||||
msgid "Examples of short topics"
|
||||
msgstr ""
|
||||
msgstr "Esempi di brevi argomenti"
|
||||
|
||||
#: templates/zerver/emails/compiled/followup_day2.html:15
|
||||
msgid ""
|
||||
@@ -1732,7 +1732,7 @@ msgstr "Non consigliati: \"Cosa pensa la gente di questo nuovo modello di design
|
||||
#: templates/zerver/emails/compiled/followup_day2.html:22
|
||||
#: templates/zerver/emails/followup_day2.source.html:21
|
||||
msgid "Example of a topic that is too long"
|
||||
msgstr ""
|
||||
msgstr "Esempio di argomento troppo lungo"
|
||||
|
||||
#: templates/zerver/emails/compiled/followup_day2.html:24
|
||||
msgid ""
|
||||
@@ -1800,7 +1800,7 @@ msgstr "Per iniziare, clicca sul bottone sotto."
|
||||
#: templates/zerver/emails/compiled/invitation_reminder.html:4
|
||||
#: templates/zerver/emails/invitation_reminder.source.html:4
|
||||
msgid "Mailbox with envelope"
|
||||
msgstr ""
|
||||
msgstr "Cassetta postale con busta"
|
||||
|
||||
#: templates/zerver/emails/compiled/invitation_reminder.html:9
|
||||
#: templates/zerver/emails/invitation_reminder.source.html:8
|
||||
@@ -2434,7 +2434,7 @@ msgstr "La migliore chat per luoghi di lavoro"
|
||||
|
||||
#: templates/zerver/for-open-source.html:23
|
||||
msgid "Zulip for open source"
|
||||
msgstr ""
|
||||
msgstr "Zulip per open source"
|
||||
|
||||
#: templates/zerver/for-working-groups-and-communities.html:18
|
||||
msgid "The best chat for working groups and communities"
|
||||
@@ -2542,7 +2542,7 @@ msgstr "Integrazioni personalizzate"
|
||||
#: templates/zerver/integrations/index.html:64
|
||||
#: templates/zerver/integrations/index.html:91
|
||||
msgid "Incoming webhooks"
|
||||
msgstr ""
|
||||
msgstr "Webhook in arrivo"
|
||||
|
||||
#: templates/zerver/integrations/index.html:67
|
||||
#: templates/zerver/integrations/index.html:94 zerver/lib/integrations.py:46
|
||||
@@ -2552,7 +2552,7 @@ msgstr "Bots interattivi"
|
||||
#: templates/zerver/integrations/index.html:70
|
||||
#: templates/zerver/integrations/index.html:97
|
||||
msgid "REST API"
|
||||
msgstr ""
|
||||
msgstr "REST API"
|
||||
|
||||
#: templates/zerver/integrations/index.html:77
|
||||
msgid "Categories"
|
||||
@@ -3431,11 +3431,11 @@ msgstr "L'evento '{event_type}' non è attualmente supportato dal webhook {webho
|
||||
|
||||
#: zerver/lib/external_accounts.py:47
|
||||
msgid "Custom external account must define url pattern"
|
||||
msgstr ""
|
||||
msgstr "L'account esterno personalizzato deve definire il modello di URL"
|
||||
|
||||
#: zerver/lib/external_accounts.py:49
|
||||
msgid "Invalid external account type"
|
||||
msgstr ""
|
||||
msgstr "Tipo di account esterno non valido"
|
||||
|
||||
#: zerver/lib/hotspots.py:12
|
||||
msgid "Reply to a message"
|
||||
@@ -3558,12 +3558,12 @@ msgstr "Token inesistente"
|
||||
#: zerver/lib/push_notifications.py:595
|
||||
#, python-format
|
||||
msgid "%(full_name)s mentioned you:"
|
||||
msgstr ""
|
||||
msgstr "%(full_name)sti ha menzionato:"
|
||||
|
||||
#: zerver/lib/push_notifications.py:597
|
||||
#, python-format
|
||||
msgid "%(full_name)s mentioned everyone:"
|
||||
msgstr ""
|
||||
msgstr "%(full_name)sha menzionato tutti:"
|
||||
|
||||
#: zerver/lib/remote_server.py:80
|
||||
#, python-format
|
||||
@@ -3678,16 +3678,16 @@ msgstr "Nome o nome utente non buono"
|
||||
#: zerver/lib/users.py:62
|
||||
#, python-format
|
||||
msgid "Invalid integration '%s'."
|
||||
msgstr ""
|
||||
msgstr "Integrazione non valida '%s'."
|
||||
|
||||
#: zerver/lib/users.py:66
|
||||
#, python-format
|
||||
msgid "Missing configuration parameters: %s"
|
||||
msgstr ""
|
||||
msgstr "Parametri di configurazione mancanti: %s"
|
||||
|
||||
#: zerver/lib/users.py:73
|
||||
msgid "Invalid {} value {} ({})"
|
||||
msgstr ""
|
||||
msgstr "Non valido {} valore {} ({})"
|
||||
|
||||
#: zerver/lib/users.py:88
|
||||
msgid "Invalid configuration data!"
|
||||
@@ -3757,7 +3757,7 @@ msgstr "%s non è un numero intero"
|
||||
#: zerver/lib/validator.py:101
|
||||
#, python-format
|
||||
msgid "Invalid %s"
|
||||
msgstr ""
|
||||
msgstr "%snon valido"
|
||||
|
||||
#: zerver/lib/validator.py:108
|
||||
#, python-format
|
||||
@@ -3816,7 +3816,7 @@ msgstr "%snon è un url"
|
||||
|
||||
#: zerver/lib/validator.py:249
|
||||
msgid "Malformed URL pattern."
|
||||
msgstr ""
|
||||
msgstr "Pattern URL non valido."
|
||||
|
||||
#: zerver/lib/validator.py:269
|
||||
#, python-brace-format
|
||||
@@ -3831,12 +3831,12 @@ msgstr "'{value}' non è una scelta valida per '{field_name}'."
|
||||
#: zerver/lib/validator.py:348
|
||||
#, python-format
|
||||
msgid "%s is not a string or an integer list"
|
||||
msgstr ""
|
||||
msgstr "%snon è una stringa o un elenco intero"
|
||||
|
||||
#: zerver/lib/validator.py:356
|
||||
#, python-format
|
||||
msgid "%s is not a string or integer"
|
||||
msgstr ""
|
||||
msgstr "%snon è una stringa o un numero intero"
|
||||
|
||||
#: zerver/lib/webhooks/common.py:51
|
||||
#, python-brace-format
|
||||
@@ -3867,11 +3867,11 @@ msgstr "L'utilizzo dell'API ha superato il limite di velocità"
|
||||
|
||||
#: zerver/models.py:255
|
||||
msgid "stream events"
|
||||
msgstr ""
|
||||
msgstr "Eventi di canale"
|
||||
|
||||
#: zerver/models.py:274
|
||||
msgid "Available on Zulip Standard. Upgrade to access."
|
||||
msgstr ""
|
||||
msgstr "Disponibile su Zulip Standard. Aggiorna per accedere."
|
||||
|
||||
#: zerver/models.py:664
|
||||
#, python-format
|
||||
@@ -3960,12 +3960,12 @@ msgstr "Non sei autorizzato a ricevere eventi da questa coda"
|
||||
#: zerver/tornado/event_queue.py:536
|
||||
#, python-format
|
||||
msgid "An event newer than %s has already been pruned!"
|
||||
msgstr ""
|
||||
msgstr "Un evento più recente di %s è già stato eliminato!"
|
||||
|
||||
#: zerver/tornado/event_queue.py:542
|
||||
#, python-format
|
||||
msgid "Event %s was not in this queue"
|
||||
msgstr ""
|
||||
msgstr "L'evento %s non era in questa coda"
|
||||
|
||||
#: zerver/tornado/exceptions.py:14
|
||||
#, python-brace-format
|
||||
@@ -4067,7 +4067,7 @@ msgstr "User-Agent header mancante dalla richiesta"
|
||||
|
||||
#: zerver/views/custom_profile_fields.py:37
|
||||
msgid "Label cannot be blank."
|
||||
msgstr ""
|
||||
msgstr "L'etichetta non può essere vuota."
|
||||
|
||||
#: zerver/views/custom_profile_fields.py:53
|
||||
msgid "Field must have at least one choice."
|
||||
@@ -4080,11 +4080,11 @@ msgstr "Tipo campo non valido."
|
||||
#: zerver/views/custom_profile_fields.py:112
|
||||
#: zerver/views/custom_profile_fields.py:150
|
||||
msgid "A field with that label already exists."
|
||||
msgstr ""
|
||||
msgstr "Un campo con quell'etichetta esiste già."
|
||||
|
||||
#: zerver/views/custom_profile_fields.py:143
|
||||
msgid "Default custom field cannot be updated."
|
||||
msgstr ""
|
||||
msgstr "Il campo personalizzato predefinito non può essere aggiornato."
|
||||
|
||||
#: zerver/views/hotspots.py:17
|
||||
#, python-format
|
||||
@@ -4268,7 +4268,7 @@ msgstr "Almeno un metodo di autenticazione deve essere abilitato."
|
||||
|
||||
#: zerver/views/realm.py:95
|
||||
msgid "Invalid video_chat_provider {}"
|
||||
msgstr ""
|
||||
msgstr "video_chat_provider {}non valido"
|
||||
|
||||
#: zerver/views/realm.py:100 zerver/views/realm_domains.py:28
|
||||
msgid "Invalid domain: {}"
|
||||
@@ -4322,16 +4322,16 @@ msgstr "Caricamento immagine fallito."
|
||||
|
||||
#: zerver/views/realm_export.py:40
|
||||
msgid "Exceeded rate limit."
|
||||
msgstr ""
|
||||
msgstr "Limite di velocità superato."
|
||||
|
||||
#: zerver/views/realm_export.py:47
|
||||
#, python-format
|
||||
msgid "Please request a manual export from %s."
|
||||
msgstr ""
|
||||
msgstr "Si prega di richiedere un'esportazione manuale da %s."
|
||||
|
||||
#: zerver/views/realm_export.py:76
|
||||
msgid "Invalid data export ID"
|
||||
msgstr ""
|
||||
msgstr "ID esportazione dati non valido"
|
||||
|
||||
#: zerver/views/realm_export.py:80
|
||||
msgid "Export already deleted"
|
||||
@@ -4505,7 +4505,7 @@ msgstr "Password errata!"
|
||||
|
||||
#: zerver/views/user_settings.py:75
|
||||
msgid "New password is too weak!"
|
||||
msgstr ""
|
||||
msgstr "La nuova password è troppo semplice!"
|
||||
|
||||
#: zerver/views/user_settings.py:104
|
||||
msgid "Check your email for a confirmation link. "
|
||||
@@ -4626,7 +4626,7 @@ msgstr "Input JSON rovinato"
|
||||
|
||||
#: zerver/webhooks/papertrail/view.py:25
|
||||
msgid "Missing expected keys"
|
||||
msgstr ""
|
||||
msgstr "Chiavi previste mancanti"
|
||||
|
||||
#: zerver/webhooks/pivotal/view.py:178
|
||||
msgid "Unable to handle Pivotal payload"
|
||||
@@ -4675,4 +4675,4 @@ msgstr "I dati sono fuori servizio."
|
||||
|
||||
#: zilencer/views.py:181
|
||||
msgid "Invalid data."
|
||||
msgstr ""
|
||||
msgstr "Dati non validi."
|
||||
|
@@ -156,7 +156,7 @@
|
||||
"Custom filter added!": "Aggiunto filtro personalizzato!",
|
||||
"Custom profile fields": "Campi profilo personalizzati",
|
||||
"Customize profile picture": "",
|
||||
"Data exports": "",
|
||||
"Data exports": "Esportazioni di dati",
|
||||
"Date uploaded": "Data di caricamento",
|
||||
"Day": "Giorno",
|
||||
"Day mode": "Modalità giorno",
|
||||
@@ -513,7 +513,7 @@
|
||||
"Show API key": "",
|
||||
"Show counts for starred messages": "Mostra conteggi sui messaggi preferiti",
|
||||
"Show fewer": "",
|
||||
"Show more": "",
|
||||
"Show more": "Mostra di più",
|
||||
"Show previews of linked websites": "Mostra anteprime dei siti collegati",
|
||||
"Show previews of uploaded and linked images": "Mostra anteprime delle immagini caricate e collegate",
|
||||
"Show starred message count": "",
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,51 +1,51 @@
|
||||
{
|
||||
"\"__file_name__\" was too large; the maximum file size is __file_size__MB.": "",
|
||||
"\"__file_name__\" was too large; the maximum file size is __file_size__MB.": "\"__file_name__\" слишком большой файл; максимальный размер файлов __file_size__мб.",
|
||||
"(This user has been deactivated)": "(Этот пользователь был отключен)",
|
||||
"(no topic)": "(без темы)",
|
||||
"(unavailable)": "",
|
||||
"(unavailable)": "(недоступно)",
|
||||
"(you)": "(вы)",
|
||||
"/me is excited (Display action text)": "",
|
||||
"/poll Where should we go to lunch today? (Create a poll)": "",
|
||||
"/me is excited (Display action text)": "/me в восторге (показать текст действия)",
|
||||
"/poll Where should we go to lunch today? (Create a poll)": "/poll Куда нам сегодня пойти пообедать? (провести голосование)",
|
||||
"1 day": "1 день",
|
||||
"1 hour": "1 час",
|
||||
"1 week": "1 неделя",
|
||||
"10 minutes": "10 минут",
|
||||
"12-hour clock (5:00 PM)": "",
|
||||
"12-hour clock (5:00 PM)": "12-часовые часы (5:00 PM)",
|
||||
"2 minutes": "2 минуты",
|
||||
"24-hour clock (17:00)": "",
|
||||
"3 days": "",
|
||||
"<a href=\"/help/export-your-organization\" target=\"_blank\">Click here</a> to learn about exporting private streams and messages.": "",
|
||||
"<a href=\"/upgrade\" target=\"_blank\">Upgrade</a> for more space.": "",
|
||||
"24-hour clock (17:00)": "24-часовые часы (17:00)",
|
||||
"3 days": "3 дня",
|
||||
"<a href=\"/help/export-your-organization\" target=\"_blank\">Click here</a> to learn about exporting private streams and messages.": "<a href=\"/help/export-your-organization\" target=\"_blank\">Нажмите тут</a>, чтобы узнать об экспорте закрытых каналов и сообщений.",
|
||||
"<a href=\"/upgrade\" target=\"_blank\">Upgrade</a> for more space.": "<a href=\"/upgrade\" target=\"_blank\">Апгрейд</a>, чтобы получить больше места.",
|
||||
"<b>Private, protected history:</b> must be invited by a member; new members can only see messages sent after they join; hidden from non-administrator users": "<b>Закрытый, защищенная переписка:</b> должен быть приглашён участником; новые пользователи могут видеть только сообщения, отправленные после их подключения; скрыт от всех пользователей не администраторов",
|
||||
"<b>Private, shared history:</b> must be invited by a member; new members can view complete message history; hidden from non-administrator users": "<b>Закрытый, открытая переписка:</b> должен быть приглашен участником; новые пользователи могут видеть всю историю переписки; скрыт от всех пользователей не администраторов",
|
||||
"<b>Public:</b> anyone can join; anyone can view complete message history without joining": "<b>Открытый:</b> любой может присоединиться; любой может читать историю сообщения не присоединяясь",
|
||||
"<p>Stream will be announced in <b>#__notifications_stream__</b>.</p>": "Канал будет анонсирован в <b>#__notifications_stream__</b>\\.</p>",
|
||||
"<p>The stream <b>__stream_name__</b> does not exist.</p><p>Manage your subscriptions <a href='#streams/all'>on your Streams page</a>.</p>": "<p>Канал <b>__stream_name__</b> не существует\\.</p><p>Управляй своими подписками на <a href='#streams/all'>своей странице Каналы</a>\\.</p> ",
|
||||
"<strong>__name__</strong> is not subscribed to this stream. They will not be notified if you mention them.": "<strong>__name__</strong> не подписан(-ы) на этот канал. Они не будут уведомлены, если Вы упомянете их.",
|
||||
"<strong>__name__</strong> is not subscribed to this stream. They will not be notified unless you subscribe them.": "<strong>__name__</strong> не подписан(ы) на этот канал. Они не будут получать уведомления, пока вы их не подпишете.",
|
||||
"A stream needs to have a name": "Укажите название канала",
|
||||
"<strong>__name__</strong> is not subscribed to this stream. They will not be notified if you mention them.": "<strong>__name__</strong> не подписан на этот канал. Пользователь не будет уведомлен, если вы упомянете его.",
|
||||
"<strong>__name__</strong> is not subscribed to this stream. They will not be notified unless you subscribe them.": "<strong>__name__</strong> не подписан на этот канал. Пользователь не будет получать уведомления, пока вы его не подпишете.",
|
||||
"A stream needs to have a name": "Канал должен иметь название",
|
||||
"A stream with this name already exists": "Канал с таким именем уже существует",
|
||||
"A wide image for the upper left corner of the app.": "",
|
||||
"API key": "API ключ",
|
||||
"A wide image for the upper left corner of the app.": "Широкое изображение для верхнего левого угла приложения.",
|
||||
"API key": "API-ключ",
|
||||
"Action": "Действие",
|
||||
"Actions": "Действия",
|
||||
"Active": "Активный",
|
||||
"Active bots": "Активные боты",
|
||||
"Active now": "Активно сейчас",
|
||||
"Active now": "Сейчас активен",
|
||||
"Add": "Добавить",
|
||||
"Add a new alert word": "Добавить новое сигнальное слово",
|
||||
"Add a new bot": "Добавить нового бота",
|
||||
"Add a new emoji": "Добавить эмодзи",
|
||||
"Add a new linkifier": "",
|
||||
"Add a new linkifier": "Добавить новый фильтр",
|
||||
"Add a new profile field": "Добавить новое поле профиля",
|
||||
"Add a new user group": "Добавить новую группу пользователей",
|
||||
"Add alert word": "Добавить сигнальное слово",
|
||||
"Add choice": "",
|
||||
"Add choice": "Новый выбор",
|
||||
"Add emoji": "Добавить эмодзи",
|
||||
"Add emoji reaction": "Добавить эмодзи реакцию",
|
||||
"Add emoji reaction (:)": "Добавить эмодзи реакцию (:)",
|
||||
"Add emoji reaction": "Добавить эмодзи-реакцию",
|
||||
"Add emoji reaction (:)": "Добавить эмодзи-реакцию (:)",
|
||||
"Add extra emoji for members of the __realm_name__ organization.": "Добавить дополнительные эмодзи для участников организации __realm_name__.",
|
||||
"Add linkifier": "",
|
||||
"Add linkifier": "Добавить фильтр",
|
||||
"Add member...": "Добавить участника...",
|
||||
"Add new default stream": "Добавить новый канал по умолчанию",
|
||||
"Add profile field": "Добавить поле профиля",
|
||||
@@ -55,47 +55,47 @@
|
||||
"Add video call": "Добавить видеовызов",
|
||||
"Added successfully!": "Добавлено успешно!",
|
||||
"Administrator": "Администратор",
|
||||
"Administrators can always delete any message.": "Администраторы могут всегда удалить любое сообщение.",
|
||||
"Admins": "",
|
||||
"Admins and full members": "",
|
||||
"Admins and members": "",
|
||||
"Admins and members, but only admins can add generic bots": "",
|
||||
"Administrators can always delete any message.": "Администраторы всегда могут удалять любое сообщение.",
|
||||
"Admins": "Администраторы",
|
||||
"Admins and full members": "Администраторы и полноценные участники",
|
||||
"Admins and members": "Администраторы и участники",
|
||||
"Admins and members, but only admins can add generic bots": "Администраторы и участники, но только администраторы могут добавлять общие боты",
|
||||
"Admins only": "Только администраторы",
|
||||
"Admins, members, and guests": "",
|
||||
"Admins, members, and guests": "Администраторы, участники и гости",
|
||||
"Alert word": "Сигнальное слово",
|
||||
"Alert word added successfully!": "Сигнальное слово успешно добавлено!",
|
||||
"Alert word already exists!": "Сигнальное слово уже существует!",
|
||||
"Alert word can't be empty!": "Сигнальное слово не может быть пустым!",
|
||||
"Alert word removed successfully!": "Сигнальное слово успешно удалено!",
|
||||
"Alert words allow you to be notified as if you were @-mentioned when certain words or phrases are used in Zulip. Alert words are not case sensitive.": "",
|
||||
"All stream members can post.": "",
|
||||
"Alert words allow you to be notified as if you were @-mentioned when certain words or phrases are used in Zulip. Alert words are not case sensitive.": "Слова предупреждения позволяют получать уведомления, как если бы вы упоминали @ при использовании определенных слов или фраз в Zulip. Оповещения не чувствительны к регистру.",
|
||||
"All stream members can post.": "Все участники потока могут оставлять сообщения.",
|
||||
"All streams": "Все каналы",
|
||||
"All unreads": "",
|
||||
"Allow message content in missed message emails": "",
|
||||
"All unreads": "Все непрочтенные",
|
||||
"Allow message content in missed message emails": "Допускать содержание сообщений в уведомлениях о пропущенных сообщениях по электронной почте",
|
||||
"Allow message deleting": "Разрешить удаление сообщений",
|
||||
"Allow message editing": "Разрешить редактирование сообщений",
|
||||
"Allow subdomains": "Разрешать поддомены",
|
||||
"Allowed domains": "Разрешенные домены",
|
||||
"Allowed domains: __domains__": "Разрешенные домены: __domains__",
|
||||
"Already subscribed to __stream__": "Уже подписан на __stream__",
|
||||
"Always": "",
|
||||
"An API key can be used to programmatically access a Zulip account. Anyone with access to your API key has the ability to read your messages, send messages on your behalf, and otherwise impersonate you on Zulip, so you should guard your API key as carefully as you guard your password. <br /> We recommend creating bots and using the bots' accounts and API keys to access the Zulip API, unless the task requires access to your account.": "API ключ может быть использован для программного доступа к аккаунту Zulip. Обладающий вашим API ключом имеет возможность читать ваши сообщения, посылать сообщения от вашего имени и иначе представлять вас в Zulip. Вам следует защищать API ключ так же как и ваш пароль. <br /> Мы рекомендуем использовать для ботов специальные аккаунты и API ключи для доступа к Zulip API, в случае если задача не требует доступа именно к вашему аккаунту.",
|
||||
"Always": "Всегда",
|
||||
"An API key can be used to programmatically access a Zulip account. Anyone with access to your API key has the ability to read your messages, send messages on your behalf, and otherwise impersonate you on Zulip, so you should guard your API key as carefully as you guard your password. <br /> We recommend creating bots and using the bots' accounts and API keys to access the Zulip API, unless the task requires access to your account.": "API-ключ может быть использован для программного доступа к учетной записи Zulip. Обладающий вашим API-ключом имеет возможность читать ваши сообщения, посылать сообщения от вашего имени и иначе представлять вас в Zulip. Вам следует защищать API-ключ так же, как и ваш пароль. <br /> Мы рекомендуем использовать для ботов специальные учетные записи и API-ключи для доступа к Zulip API, в случае если задача не требует доступа именно к вашей учетной записи.",
|
||||
"An hour ago": "Час назад",
|
||||
"An unknown error occurred.": "Произошла неизвестная ошибка.",
|
||||
"Announce stream": "Анонсировать новый канал",
|
||||
"Any member of this organization can add custom emoji.": "",
|
||||
"Any organization administrator can conduct an export.": "",
|
||||
"Any member of this organization can add custom emoji.": "Любой член этой организации может добавить пользовательские эмодзи.",
|
||||
"Any organization administrator can conduct an export.": "Любой администратор организации может экспортировать данные.",
|
||||
"Any time": "Любое время",
|
||||
"Anyone in this organization can add bots": "Любой в этой организации может добавлять ботов",
|
||||
"Are invitations required for joining the organization?": "Необходимо ли приглашение чтобы присоединиться к организации?",
|
||||
"Are invitations required for joining the organization?": "Необходимо ли приглашение, чтобы присоединиться к организации?",
|
||||
"Are you sure you want to create stream '__stream_name__' and subscribe __count__ users to it?": "Вы уверены, что хотите создать канал '__stream_name__' и подписать на него __count__ пользователей?",
|
||||
"Are you sure you want to delete <b>__group_name__</b>?": "",
|
||||
"Are you sure you want to delete all messages in <b>__topic_name__</b>?": "",
|
||||
"Are you sure you want to delete <b>__group_name__</b>?": "Вы уверены, что хотите удалить <b>__group_name__</b>?",
|
||||
"Are you sure you want to delete all messages in <b>__topic_name__</b>?": "Вы уверены, что хотите удалить все сообщения в <b>1__topic_name__</b>?",
|
||||
"Are you sure you want to do this?": "Вы уверены, что хотите сделать это?",
|
||||
"Are you sure you want to mention all <strong>__count__</strong> people in this stream?": "Вы уверены, что хотите упомянуть всех <strong>__count__</strong> участников канала?",
|
||||
"Are you sure you want to resend the invitation to <strong><span class=\"email\"></span></strong>?": "Вы действительно хотите отправить повторное приглашение <strong><span class=\\\\\"email\\\\\"></span></strong>?",
|
||||
"Are you sure you want to revoke the invitation to <strong>__email__</strong>?": "",
|
||||
"Are you sure you want to revoke this invitation link created by <strong>__referred_by__</strong>?": "",
|
||||
"Are you sure you want to resend the invitation to <strong><span class=\"email\"></span></strong>?": "Вы действительно хотите отправить повторное приглашение <strong><span class=\"email\"></span></strong>?",
|
||||
"Are you sure you want to revoke the invitation to <strong>__email__</strong>?": "Вы уверены, что хотите отозвать приглашение для <strong>__email__</strong>?,",
|
||||
"Are you sure you want to revoke this invitation link created by <strong>__referred_by__</strong>?": "Вы уверены, что хотите отозвать приглашение по ссылке <strong>__referred_by__</strong>?",
|
||||
"Attach files": "Прикрепить файлы",
|
||||
"Attachment deleted": "Прикрепленный файл удален",
|
||||
"Audible desktop notifications": "Звуковые уведомления",
|
||||
@@ -104,16 +104,16 @@
|
||||
"Automatic": "Автоматически",
|
||||
"Avatar from Gravatar": "Аватар из Gravatar",
|
||||
"Bot": "Бот",
|
||||
"Bot email": "Email бота",
|
||||
"Bot email (a-z, 0-9, and dashes only)": "",
|
||||
"Bot email": "Адрес электронной почты бота",
|
||||
"Bot email (a-z, 0-9, and dashes only)": "Адрес электронной почты бота (только a-z, 0-9 и тире)",
|
||||
"Bot type": "Тип бота",
|
||||
"Bots": "Боты",
|
||||
"By deactivating <strong><span class=\"user_name\"></span></strong> <<span class=\"email\"></span>>, they will be logged out immediately.": "Если вы отключите учетную запись <strong><span class=\"user_name\"></span></strong> <<span class=\"email\"></span>>, они автоматически выйдут из системы.",
|
||||
"By deactivating <strong><span class=\"user_name\"></span></strong> <<span class=\"email\"></span>>, they will be logged out immediately.": "Если вы отключите учетную запись <strong><span class=\"user_name\"></span></strong> <<span class=\"email\"></span>>, пользователь немедленно будет отключен от системы.",
|
||||
"By deactivating your account, you will be logged out immediately.": "Отключив свою учетную запись, вы автоматически выйдете из системы.",
|
||||
"Cancel": "Отмена",
|
||||
"Change": "Изменить",
|
||||
"Change bot info and owner": "",
|
||||
"Change email": "Изменить email",
|
||||
"Change bot info and owner": "Изменить информацию и владельца бота",
|
||||
"Change email": "Изменить адрес электронной почты",
|
||||
"Change full name": "Изменить полное имя",
|
||||
"Change later messages to this topic": "Изменить последующие сообщения в этой теме",
|
||||
"Change notification settings for individual streams on your <a href=\"/#streams\">Streams page</a>.": "Измените настройки уведомлений для отдельных каналов на странице <a href=\"/#streams\">Каналы</a>.",
|
||||
@@ -123,76 +123,76 @@
|
||||
"Change stream permissions for #": "Изменить разрешения канала #",
|
||||
"Change user info and roles": "Изменить информацию о пользователе и его функции",
|
||||
"Check all": "Выбрать все",
|
||||
"Check your email (%s) to confirm the new address.": "",
|
||||
"Check your email (%s) to confirm the new address.": "Для подтверждения нового адреса проверьте свою электронную почту (%s) ",
|
||||
"Choose avatar": "Выберите аватар",
|
||||
"Choose custom color": "Выберите произвольный цвет",
|
||||
"Clear emoji image": "Очистить изображение эмодзи",
|
||||
"Clear profile picture": "",
|
||||
"Clear profile picture": "Очистить картинку профиля",
|
||||
"Click anywhere on a message to reply.": "Кликните в любое место сообщения чтобы ответить.",
|
||||
"Click outside the input box to save. We\\'ll automatically notify anyone that was added or removed.": "Для сохранения нажмите вне поля ввода. Мы автоматически уведомим всех, кто был добавлен или удален.",
|
||||
"Close": "Закрыть",
|
||||
"Collapse": "Свернуть",
|
||||
"Compose your message here": "Введите ваше сообщение здесь",
|
||||
"Condense message (-)": "",
|
||||
"Condense message (-)": "Сжать сообщение (-)",
|
||||
"Configure regular expression patterns that will be automatically linkified when used in Zulip message bodies or topics. For example to automatically linkify commit IDs and issue numbers (e.g. #123) to the corresponding items in a GitHub project, you could use the following:": "Настройте шаблоны регулярных выражений, которые будут автоматически вставлять ссылки при использовании в телах или темах сообщений Zulip. Например, чтобы автоматически привязывать идентификаторы коммитов и номера проблем (например, #123) к соответствующим элементам проекта GitHub, вы можете использовать следующее:",
|
||||
"Configure the authentication methods for your organization.": "Настроить метод аутентификации для вашей организации",
|
||||
"Configure the default streams new users are subscribed to when joining your organization.": "Настроить каналы по умолчанию на которые будут подписаны новые пользователи вашей организации.",
|
||||
"Convert emoticons before sending (<code>:)</code> becomes \ud83d\ude03)": "Превращать эмотиконы перед отправкой (<code>:)</code> становится 😃)",
|
||||
"Cookie Bot": "Cookie бот",
|
||||
"Convert emoticons before sending (<code>:)</code> becomes \ud83d\ude03)": "Превращать эмодзи перед отправкой (<code>:)</code> становится 😃)",
|
||||
"Cookie Bot": "Cookie-бот",
|
||||
"Copied!": "Скопировано!",
|
||||
"Copy and close": "Скопировать и закрыть",
|
||||
"Copy from stream": "Копировать из канала",
|
||||
"Copy link to conversation": "Скопировать ссылку в беседу",
|
||||
"Copy zuliprc": "Копировать zuliprc",
|
||||
"Create": "Создать",
|
||||
"Create bot": "Создать бота",
|
||||
"Create bot": "Создать бот",
|
||||
"Create new stream": "Создать новый канал",
|
||||
"Create stream": "Создать канал",
|
||||
"Creating bot": "Создаю бота",
|
||||
"Creating bot": "Создаю бот",
|
||||
"Creating stream...": "Создаю канал...",
|
||||
"Current password": "Текущий пароль",
|
||||
"Custom": "",
|
||||
"Custom": "Свое",
|
||||
"Custom emoji added!": "Дополнительный эмодзи добавлен!",
|
||||
"Custom filter added!": "Дополнительный фильтр добавлен!",
|
||||
"Custom profile fields": "Дополнительные поля профиля",
|
||||
"Customize profile picture": "",
|
||||
"Data exports": "",
|
||||
"Customize profile picture": "Настроить изображение профиля",
|
||||
"Data exports": "Выгрузка данных",
|
||||
"Date uploaded": "Дата загрузки",
|
||||
"Day": "День",
|
||||
"Day mode": "Дневной режим",
|
||||
"Day of the week to send digests": "",
|
||||
"Day of the week to send digests": "День недели для отправки дайджеста",
|
||||
"Deactivate": "Отключить",
|
||||
"Deactivate account": "Отключить учетную запись",
|
||||
"Deactivate now": "Отключить сейчас",
|
||||
"Deactivate organization": "Отключить организацию",
|
||||
"Deactivate your account": "Отключить вашу учетную запись",
|
||||
"Deactivated": "",
|
||||
"Deactivated": "Отключено",
|
||||
"Deactivated users": "Отключенные пользователи",
|
||||
"Deactivation encountered an error. Please reload and try again.": "Ошибка деактивации. Перезагрузите и попробуйте еще раз.",
|
||||
"Default language": "Язык по умолчанию",
|
||||
"Default settings for new users joining this organization.": "",
|
||||
"Default settings for new users joining this organization.": "Настройки по умолчанию для новых пользователей этой организации.",
|
||||
"Default user settings": "Настройки пользователя по умолчанию",
|
||||
"Delete": "Удалить",
|
||||
"Delete alert word": "Удалить сигнальное слово",
|
||||
"Delete all messages in <b>__topic_name__</b>": "",
|
||||
"Delete bot": "Удалить бота",
|
||||
"Delete all messages in <b>__topic_name__</b>": "Удалить все сообщения в <b>__topic_name__</b>",
|
||||
"Delete bot": "Удалить бот",
|
||||
"Delete draft": "Удалить черновик",
|
||||
"Delete file": "Удалить файл",
|
||||
"Delete logo": "Удалить логотоп",
|
||||
"Delete message": "Удалить сообщение",
|
||||
"Delete messages": "Удалить сообщения",
|
||||
"Delete profile picture": "",
|
||||
"Delete profile picture": "Удалить картинку профиля",
|
||||
"Delete stream": "Удалить канал",
|
||||
"Delete topic": "Удалить тему",
|
||||
"Delete user group": "Удалить группу пользователей",
|
||||
"Deleted successfully!": "Удалено успешно!",
|
||||
"Deleting this stream will immediately unsubscribe everyone, and the stream's content will not be recoverable.": "Удаление этого канала моментально отпишет от него всех участников, а сообщения в канале будут безвозвратно удалены.",
|
||||
"Demote inactive streams": "",
|
||||
"Demote inactive streams": "Убирать неактивные каналы",
|
||||
"Dense mode": "Сжатый режим",
|
||||
"Depending on the size of your organization, an export can take anywhere from seconds to an hour.": "",
|
||||
"Depending on the size of your organization, an export can take anywhere from seconds to an hour.": "В зависимости от размера вашей организации, экспорт может занять от нескольких секунд до часа.",
|
||||
"Description": "Описание",
|
||||
"Desktop": "",
|
||||
"Desktop notifications are triggered for messages that are offscreen when they arrive. Mobile and email notifications are triggered once you have been away from Zulip for a few minutes.": "",
|
||||
"Desktop": "Рабочий стол",
|
||||
"Desktop notifications are triggered for messages that are offscreen when they arrive. Mobile and email notifications are triggered once you have been away from Zulip for a few minutes.": "Всплывающие уведомления на рабочем столе вызываются сообщениями, которые в момент поступления не отображаются на экране. Уведомления на мобильных устройствах и по электронной почте вызываются, когда вы уже несколько минут не присутствуете в Zulip.",
|
||||
"Disabled": "Отключен",
|
||||
"Discard": "Сбросить",
|
||||
"Discard changes": "Отменить изменения",
|
||||
@@ -204,23 +204,23 @@
|
||||
"Download": "Скачать",
|
||||
"Download .zuliprc": "Скачать .zuliprc",
|
||||
"Download botserverrc": "Скачать botserverrc",
|
||||
"Download config of all active outgoing webhook bots in Zulip Botserver format.": "Загрузить конфигурацию всех исходящих вебхук ботов в формате Zulip Botserver",
|
||||
"Download config of all active outgoing webhook bots in Zulip Botserver format.": "Загрузить конфигурацию всех исходящих вебхук-ботов в формате Zulip Botserver",
|
||||
"Download file": "Скачать файл",
|
||||
"Download zuliprc": "Скачать zuliprc",
|
||||
"Drafts": "Черновики",
|
||||
"Drafts older than <strong>__draft_lifetime__</strong> days are automatically removed.": "",
|
||||
"Drafts older than <strong>__draft_lifetime__</strong> days are automatically removed.": "Черновики старше<strong>__draft_lifetime__</strong> дней автоматически удаляются.",
|
||||
"EDITED": "ИЗМЕНЕНО",
|
||||
"Edit": "Изменить",
|
||||
"Edit bot": "Изменить бота",
|
||||
"Edit bot": "Изменить бот",
|
||||
"Edit status message": "Редактировать статус",
|
||||
"Edit user": "Изменить пользователя",
|
||||
"Edit your profile": "Редактировать свой профиль",
|
||||
"Edited (__last_edit_timestr__)": "Изменено (__last_edit_timestr__)",
|
||||
"Email": "Адрес email",
|
||||
"Email address": "Адрес email",
|
||||
"Email address changes are disabled in this organization.": "Изменение адреса email отключено в этой организации.",
|
||||
"Email": "Адрес электронной почты",
|
||||
"Email address": "Адрес электронной почты",
|
||||
"Email address changes are disabled in this organization.": "Изменение адреса электронной почты отключено в этой организации.",
|
||||
"Email copied": "Электронная почта скопирована",
|
||||
"Email notifications": "Уведомления на email",
|
||||
"Email notifications": "Уведомления на электронную почту",
|
||||
"Emoji name": "Название эмодзи",
|
||||
"Emojiset changed successfully!": "Набор эмодзи изменен успешно.",
|
||||
"Enable message edit history": "Включить историю редактирования сообщений",
|
||||
@@ -239,13 +239,13 @@
|
||||
"Error removing subscription": "Ошибка удаления подписки",
|
||||
"Error removing user from this stream.": "Ошибка удаления пользователя с канала.",
|
||||
"Error saving edit": "Ошибка сохранения изменений",
|
||||
"Error: Cannot deactivate the only organization administrator.": "",
|
||||
"Error: Cannot deactivate the only organization administrator.": "Ошибка: Нельзя деактивировать единственного администратора организации.",
|
||||
"Estimated messages per week": "Примерно сообщений в неделю",
|
||||
"Expand message (-)": "",
|
||||
"Export failed": "",
|
||||
"Export started. Check back in a few minutes.": "",
|
||||
"Exports all users, settings, and all data visible in public streams.": "",
|
||||
"External account type": "",
|
||||
"Expand message (-)": "Развернуть сообщения (-)",
|
||||
"Export failed": "Ошибка экспорта",
|
||||
"Export started. Check back in a few minutes.": "Экспорт данных начат. Проверьте через пару минут.",
|
||||
"Exports all users, settings, and all data visible in public streams.": "Выгрузка всех пользователей, настроек, и всех данных, доступных в публичных каналах.",
|
||||
"External account type": "Тип внешнего аккаунта",
|
||||
"External link": "Внешняя ссылка",
|
||||
"Failed": "Не удалось",
|
||||
"Failed to change notifications stream!": "Не удалось изменить канал для уведомлений!",
|
||||
@@ -254,57 +254,57 @@
|
||||
"Failed!": "Не удалось!",
|
||||
"Field choices": "Выбор поля",
|
||||
"File": "Файл",
|
||||
"File and image uploads have been disabled for this organization.": "",
|
||||
"File and image uploads have been disabled for this organization.": "Для данной организации отключена возможность закачки файлов и изображений.",
|
||||
"File type is not supported.": "Тип файла не поддерживается",
|
||||
"File upload is not yet available for your browser.": "Загрузка файлов не доступна в вашем браузере.",
|
||||
"Filter": "Фильтр",
|
||||
"Filter bots": "Фильтр ботов",
|
||||
"Filter deactivated users": "Фильтр отключенных пользователей",
|
||||
"Filter emojis": "",
|
||||
"Filter exports": "",
|
||||
"Filter emojis": "Фильтр эмодзи",
|
||||
"Filter exports": "Отфильтровать",
|
||||
"Filter invites": "Фильтр приглашений",
|
||||
"Filter linkifiers": "",
|
||||
"Filter linkifiers": "Отфильтровать",
|
||||
"Filter streams": "Фильтр каналов",
|
||||
"Filter users": "Фильтр пользователей",
|
||||
"First time? Read our <a href=\"/help/getting-your-organization-started-with-zulip#create-streams\" target=\"_blank\">guidelines</a> for creating and naming streams.": "",
|
||||
"First time? Read our <a href=\"/help/getting-your-organization-started-with-zulip#create-streams\" target=\"_blank\">guidelines</a> for creating and naming streams.": "Это ваш первый раз? Прочитайте наше <a href=\"/help/getting-your-organization-started-with-zulip#create-streams\" target=\"_blank\">руководство</a> по созданию и наименованию каналов.",
|
||||
"Forgotten it?": "Забыли?",
|
||||
"Formatting": "Форматирование текста",
|
||||
"Friday": "",
|
||||
"Friday": "Пятница",
|
||||
"Full name": "Полное имя",
|
||||
"Generate invite link": "Создать ссылку для приглашения",
|
||||
"Generate new API key": "Сгенерировать новый API-ключ",
|
||||
"Generating link...": "",
|
||||
"Generating link...": "Сгенерировать ссылку",
|
||||
"Generic": "Общий",
|
||||
"Get API key": "Получить API-ключ",
|
||||
"Go back": "Вернуться",
|
||||
"Got it!": "Понял!",
|
||||
"Guest": "Гость",
|
||||
"Guests cannot edit custom emoji.": "",
|
||||
"Hide starred message count": "",
|
||||
"Guests cannot edit custom emoji.": "Гости не могут редактировать собственные эмодзи.",
|
||||
"Hide starred message count": "Скрыть количество отмеченных сообщений",
|
||||
"High contrast mode": "Высоко-контрастный режим",
|
||||
"Hint": "Совет",
|
||||
"Hint (up to 80 characters)": "Подсказка (до 80 символов)",
|
||||
"Idle": "Ожидающий",
|
||||
"Image": "Изображение",
|
||||
"Inactive bots": "Неактивные боты",
|
||||
"Include content of private messages in desktop notifications": "Получать содержимое личных сообщений во всплывающих оповещениях",
|
||||
"Include content of private messages in desktop notifications": "Получать содержимое личных сообщений во всплывающих уведомлениях",
|
||||
"Include message content in missed message emails": "Включить содержимое сообщения в пропущенные сообщения электронной почты",
|
||||
"Include organization name in subject of missed message emails": "Включить название организации в тему пропущенных сообщений электронной почты",
|
||||
"Incoming webhooks can only send messages.": "Входящий вебхук может только отправлять сообщения.",
|
||||
"Interface": "Интерфейс",
|
||||
"Invalid slash command. Check if you are missing a space after the command.": "Неверная команда косой черты. Проверьте, возможно вы пропустили пробел после команды.",
|
||||
"Invalid stream id": "Неверный код канала",
|
||||
"Invitation link: <a href=\"__link__\">__link__</a>": "",
|
||||
"Invitation link: <a href=\"__link__\">__link__</a>": "Ссылка приглашения: <a href=\"__link__\">__link__</a>",
|
||||
"Invite": "Пригласить",
|
||||
"Invite link": "",
|
||||
"Invite link": "Ссылка приглашения",
|
||||
"Invite more users": "Пригласить еще пользователей",
|
||||
"Invited as": "",
|
||||
"Invited as": "Приглашен как",
|
||||
"Invited at": "Приглашен в",
|
||||
"Invited by": "Пригласил",
|
||||
"Invites": "",
|
||||
"Invites": "Приглашения",
|
||||
"Inviting...": "Приглашаю...",
|
||||
"It's been a while! Since you were last here, you received <b>__unread_count__</b> new messages.": "С возвращением! Пока вас не было, для вас накопилось <b>__unread_count__</b> новых сообщений.",
|
||||
"Joined": "",
|
||||
"Joined": "Подключился",
|
||||
"Joining the organization": "Присоединение к организации",
|
||||
"Just now": "Только что",
|
||||
"Keyboard shortcuts": "Горячие клавиши",
|
||||
@@ -312,7 +312,7 @@
|
||||
"Language settings": "Языковые настройки",
|
||||
"Large number of subscribers": "Большое количество подписчиков",
|
||||
"Last active": "Последняя активность",
|
||||
"Last active: __last_seen__": "",
|
||||
"Last active: __last_seen__": "Последняя активность: __last_seen__",
|
||||
"Last modified": "Последнее изменение",
|
||||
"Local time": "Местное время",
|
||||
"Looking for our <a href=\"/integrations\" target=\"_blank\">Integrations</a> or <a href=\"/api\" target=\"_blank\">API</a> documentation?": "Ищите нашу документацию по <a href=\"/integrations\" target=\"_blank\">интеграции</a> или <a href=\"/api\" target=\"_blank\">API</a>? ",
|
||||
@@ -322,22 +322,22 @@
|
||||
"Mark all messages in <b>__stream.name__</b> as read": "Отметить все сообщения в <b>__stream.name__</b> как прочитанные",
|
||||
"Mark all messages in <b>__topic_name__</b> as read": "Отметить все сообщения в <b>__topic_name__</b> как прочитанные",
|
||||
"Marketing team": "Команда по маркетингу",
|
||||
"Marking all messages as read\u2026": "Пометить все сообщения как прочитанные",
|
||||
"Marking all messages as read\u2026": "Пометить все сообщения как прочитанные...",
|
||||
"Member": "Участник",
|
||||
"Mentioned in": "Упомянут в",
|
||||
"Message #__- stream_name__": "",
|
||||
"Message __- recipient_names__": "",
|
||||
"Message #__- stream_name__": "Сообщение в #__- stream_name__",
|
||||
"Message __- recipient_names__": "Сообщение __- recipient_names__",
|
||||
"Message actions": "Действия с сообщением",
|
||||
"Message editing": "Редактирование сообщений",
|
||||
"Message formatting": "Форматирование сообщений",
|
||||
"Message sent when you were not subscribed": "Сообщение было отправлено, когда вы были не подписаны на канал",
|
||||
"Messages retention period in days (blank means messages are retained forever)": "Период хранения сообщений в днях (пустое значение означает сохранять навсегда)",
|
||||
"Method": "Метод",
|
||||
"Mobile": "",
|
||||
"Mobile": "Мобильный",
|
||||
"Mobile notifications": "Мобильные уведомления",
|
||||
"Mobile push notifications are not configured on this server.": "Мобильные push-оповещения не настроены на этом сервере.",
|
||||
"Monday": "",
|
||||
"More details are available <a href=\"/help/add-a-custom-linkification-filter\" target=\"_blank\">in the Help Center article</a>.": "",
|
||||
"Mobile push notifications are not configured on this server.": "Мобильные push-уведомления не настроены на этом сервере.",
|
||||
"Monday": "Понедельник",
|
||||
"More details are available <a href=\"/help/add-a-custom-linkification-filter\" target=\"_blank\">in the Help Center article</a>.": "Более подробная информация доступна <a href=\"/help/add-a-custom-linkification-filter\" target=\"_blank\">в статье в справочном центре</a>.",
|
||||
"More than 2 weeks ago": "Более 2-х недель назад",
|
||||
"Mute stream": "Заглушить канал",
|
||||
"Mute the stream <b>__stream.name__</b>": "Заглушить поток <b>__stream.name__</b>",
|
||||
@@ -347,11 +347,11 @@
|
||||
"Muted streams don't show up in \\\"All messages\\\" or generate notifications unless you are mentioned.": "Заглушенный каналы не отображаются в \\\"Все сообщения\\\" и не показывают уведомления если вы не упомянуты.",
|
||||
"N": "N",
|
||||
"Name": "Имя",
|
||||
"Name changes are disabled in this organization. Contact an administrator to change your name.": "",
|
||||
"Name or email": "Имя или email",
|
||||
"Narrow to __- message_recipient__": "",
|
||||
"Name changes are disabled in this organization. Contact an administrator to change your name.": "Изменения имени отключено для данной организации. Чтобы изменить имя, пожалуйста, обратитесь к администротору.",
|
||||
"Name or email": "Имя или адрес электронной почты",
|
||||
"Narrow to __- message_recipient__": "Показать только получателя __- message_recipient__",
|
||||
"Narrow to stream "__display_recipient__"": "Показать только канал "__display_recipient__"",
|
||||
"Narrow to stream "__display_recipient__", topic "__topic__"": "",
|
||||
"Narrow to stream "__display_recipient__", topic "__topic__"": "Показать только тему "__topic__" в канале "__display_recipient__"",
|
||||
"Narrow to topic <b>__topic_name__</b>": "Показать только тему <b>__topic_name__</b>",
|
||||
"Narrow to your private messages with __display_reply_to__": "Показать только личную переписку с __display_reply_to__",
|
||||
"Never": "Никогда",
|
||||
@@ -359,7 +359,7 @@
|
||||
"New": "Новый",
|
||||
"New alert word": "Новое сигнальное слово",
|
||||
"New choice": "Новый выбор",
|
||||
"New email": "Новый email адрес",
|
||||
"New email": "Новый адрес электронной почты",
|
||||
"New full name": "Новое полное имя",
|
||||
"New members can only see messages sent after they join.": "Новые пользователи видят сообщения отправленные после присоединения",
|
||||
"New members can view complete message history.": "Новые пользователи видят всю историю сообщений",
|
||||
@@ -375,27 +375,27 @@
|
||||
"Night": "Ночь",
|
||||
"Night mode": "Ночной режим",
|
||||
"No": "Нет",
|
||||
"No bots match your current filter.": "Нет ботов попадающих под текущий фильтр.",
|
||||
"No custom emoji.": "",
|
||||
"No default streams match you current filter.": "Нет каналов по умолчанию, попадающих под текущий фильтр.",
|
||||
"No bots match your current filter.": "Нет ботов, подпадающих под текущий фильтр.",
|
||||
"No custom emoji.": "Нет дополнительных эмодзи.",
|
||||
"No default streams match you current filter.": "Нет каналов по умолчанию, подпадающих под текущий фильтр.",
|
||||
"No description.": "Нет описания.",
|
||||
"No drafts.": "Нет черновиков.",
|
||||
"No exports.": "",
|
||||
"No invites match your current filter.": "Нет приглашений подпадающих под текущий фильтр",
|
||||
"No linkifiers set.": "",
|
||||
"No exports.": "Нет выгрузок данных.",
|
||||
"No invites match your current filter.": "Нет приглашений, подпадающих под текущий фильтр",
|
||||
"No linkifiers set.": "Пока нет фильтров.",
|
||||
"No more topics.": "Нет больше тем.",
|
||||
"No owner": "",
|
||||
"No owner": "Без владельца",
|
||||
"No restrictions": "Нет ограничений",
|
||||
"No users match your current filter.": "Нет пользователей попадающих под текущий фильтр.",
|
||||
"No users match your current filter.": "Нет пользователей, подпадающих под текущий фильтр.",
|
||||
"None": "Пусто",
|
||||
"Note that any bots that you maintain will be disabled.": "Обратите внимание, все ваши боты будут отключены.",
|
||||
"Note that organizations are limited to five exports per week.": "",
|
||||
"Nothing to preview": "Пустое сообщение",
|
||||
"Notification sound": "",
|
||||
"Notifications": "Оповещения",
|
||||
"Notifications for @all/@everyone mentions": "",
|
||||
"Notifications stream changed!": "Оповещения канала изменены!",
|
||||
"Notifications stream disabled!": "Оповещения канала отключены!",
|
||||
"Note that any bots that you maintain will be disabled.": "Обратите внимание, что все ваши боты будут отключены.",
|
||||
"Note that organizations are limited to five exports per week.": "Пожалуйста, учтите, что организации ограничены пятью выгрузками данных за неделю.",
|
||||
"Nothing to preview": "Ничего нет для предпросмотра",
|
||||
"Notification sound": "Звук уведомления",
|
||||
"Notifications": "Уведомления",
|
||||
"Notifications for @all/@everyone mentions": "Уведомления про упоминание всех через @all/@everyone",
|
||||
"Notifications stream changed!": "Уведомления канала изменены!",
|
||||
"Notifications stream disabled!": "Уведомления канала отключены!",
|
||||
"Offline": "Не в сети",
|
||||
"Old password": "Старый пароль",
|
||||
"Only group members and organization administrators can modify a group.": "Только участники группы и администраторы организации могут редактировать группу.",
|
||||
@@ -403,12 +403,12 @@
|
||||
"Only organization administrators can add custom emoji in this organization.": "Только администраторы могут добавлять пользовательские эмодзи в эту организацию.",
|
||||
"Only organization administrators can add generic bots": "Только админы могут добавлять общих ботов",
|
||||
"Only organization administrators can edit these settings.": "Только администраторы организации могут изменять эти настройки.",
|
||||
"Only organization administrators can modify user groups in this organization.": "",
|
||||
"Only organization administrators can modify user groups in this organization.": "Только администраторы организации могут изменять группы пользователей в этой организации.",
|
||||
"Only organization administrators can post.": "Только администраторы организации могут писать",
|
||||
"Only organization admins are allowed to post to this stream.": "Только администратор организации может отправлять сообщения в этот канал.",
|
||||
"Only stream members can add users to a private stream": "",
|
||||
"Only stream members can add users to a private stream": "Только подписчики канала могут добавлять пользователей к закрытому каналу.",
|
||||
"Optional": "Необязательно",
|
||||
"Or, to automatically linkify GitHub's <code>org/repo#1234</code> syntax:": "",
|
||||
"Or, to automatically linkify GitHub's <code>org/repo#1234</code> syntax:": "Или автоматически фильтровать синтаксис GitHub <code>org/repo#1234</code>:",
|
||||
"Organization": "Организация",
|
||||
"Organization administrator": "Администратор организации",
|
||||
"Organization administrators can change this in the organization settings.": "Администраторы организации могут изменить это в настройках организации.",
|
||||
@@ -416,15 +416,15 @@
|
||||
"Organization logo": "Логотип организации",
|
||||
"Organization name": "Название организации",
|
||||
"Organization profile": "Профиль организации",
|
||||
"Organization profile picture": "",
|
||||
"Organization profile picture": "Картинка профиля организации",
|
||||
"Organization settings": "Настройки организации",
|
||||
"Organization using __percent_used__% of __upload_quota__.": "",
|
||||
"Other notification settings": "Другие настройки оповещени",
|
||||
"Organization using __percent_used__% of __upload_quota__.": "Организация использует __percent_used__% своих __upload_quota__.",
|
||||
"Other notification settings": "Другие настройки уведомлений",
|
||||
"Other permissions": "Другие разрешения",
|
||||
"Other settings": "Другие настройки",
|
||||
"Outgoing webhook message format": "Формат сообщения исходящего вебхука",
|
||||
"Owner": "Владелец",
|
||||
"Owner: __name__": "",
|
||||
"Owner: __name__": "Владелец:__name__",
|
||||
"Password": "Пароль",
|
||||
"Password is too weak": "Пароль слишком простой",
|
||||
"Password should be at least __length__ characters long": "Длина пароля должна быть не менее __length__ символов",
|
||||
@@ -439,18 +439,18 @@
|
||||
"Please specify a stream": "Укажите канал",
|
||||
"Please specify a topic": "Укажите тему",
|
||||
"Please specify at least one valid recipient": "Укажите хотя бы одного получателя",
|
||||
"Press > for list of topics": "",
|
||||
"Prevent users from changing their avatar": "",
|
||||
"Prevent users from changing their email address": "Запретить пользователям изменять свой email адрес",
|
||||
"Press > for list of topics": "Нажмите > для списка тем",
|
||||
"Prevent users from changing their avatar": "Запретить пользователям изменять свой аватар",
|
||||
"Prevent users from changing their email address": "Запретить пользователям изменять свой адрес электронной почты",
|
||||
"Prevent users from changing their name": "Запретить пользователям изменять свое имя",
|
||||
"Preview": "Предпросмотр",
|
||||
"Preview organization profile": "",
|
||||
"Preview organization profile": "Предпросмотр профиля организации",
|
||||
"Preview profile": "Предпросмотр профиля",
|
||||
"Private messages and mentions": "",
|
||||
"Private messages, @-mentions, and alert words": "",
|
||||
"Pro tip: You can use 'd' to open your drafts.": "",
|
||||
"Private messages and mentions": "Личные сообщения и упоминания",
|
||||
"Private messages, @-mentions, and alert words": "Личные сообщения, упоминания через @ и сигнальные слова",
|
||||
"Pro tip: You can use 'd' to open your drafts.": "Совет профессионала: вы можете нажать клавишу 'd', чтобы открыть свои черновики.",
|
||||
"Profile": "Профиль",
|
||||
"Profile picture": "",
|
||||
"Profile picture": "Картинка профиля",
|
||||
"Quote and reply": "Ответить с цитированием",
|
||||
"Reactivate": "Активировать",
|
||||
"Reactivate bot": "Включить бота",
|
||||
@@ -461,7 +461,7 @@
|
||||
"Remove from default": "Удалить из списка по умолчанию",
|
||||
"Reply (r)": "Ответ (r)",
|
||||
"Reply mentioning user": "Ответить упомянув пользователя",
|
||||
"Requesting user": "",
|
||||
"Requesting user": "Запрашиваем пользователя",
|
||||
"Require topics in stream messages": "Требовать тему в сообщениях канала",
|
||||
"Resend": "Переслать",
|
||||
"Resend invitation to <span class=\"email\"></span>": "Повторно пригласить <span class=\"email\"></span>",
|
||||
@@ -473,12 +473,12 @@
|
||||
"Restrict to a list of domains": "Ограничить список доменов",
|
||||
"Retry": "Повторить",
|
||||
"Revoke": "Отозвать",
|
||||
"Revoke invitation link": "",
|
||||
"Revoke invitation to __email__": "",
|
||||
"Revoke invitation link": "Отозвать ссылку для приглашения",
|
||||
"Revoke invitation to __email__": "Отозвать приглашение __email__",
|
||||
"Revoke now": "Отозвать сейчас",
|
||||
"Role": "Роль",
|
||||
"SAVING": "",
|
||||
"Saturday": "",
|
||||
"SAVING": "СОХРАНЕНИЕ",
|
||||
"Saturday": "Суббота",
|
||||
"Save": "Сохранить",
|
||||
"Save changes": "Сохранить изменения",
|
||||
"Save failed": "Ошибка сохранения",
|
||||
@@ -493,30 +493,30 @@
|
||||
"Select date and time": "Выбрать дату и время",
|
||||
"Select default language": "Выбрать язык по умолчанию",
|
||||
"Send digest emails when I'm away": "Отправлять дайджест на почту, когда меня нет",
|
||||
"Send email notifications for new logins to my account": "Посылать email-уведомления о новых логинах на мою учётную запись",
|
||||
"Send email notifications for new logins to my account": "Посылать уведомления о новых логинах на мою учётную запись по электронной почте",
|
||||
"Send emails introducing Zulip to new users": "Отправлять ознакомительное письмо о Zulip новым пользователям",
|
||||
"Send mobile notifications even if I'm online (useful for testing)": "",
|
||||
"Send mobile notifications even if I'm online (useful for testing)": "Посылать уведомления на мобильные устройства, даже если я онлайн (полезно для тестирования)",
|
||||
"Send private message": "Отправить личное сообщение",
|
||||
"Send weekly digest emails to inactive users": "",
|
||||
"Send weekly digest emails to inactive users": "Посылать еженедельный обзор по электронной почте неактивным пользователям",
|
||||
"Sent!": "Отправлено!",
|
||||
"Sent! Scroll down to view your message.": "",
|
||||
"Sent! Your message is outside your current narrow.": "",
|
||||
"Sent! Your message was sent to a stream you have muted.": "",
|
||||
"Sent! Your message was sent to a topic you have muted.": "",
|
||||
"Sent! Your recent message is outside the current search.": "",
|
||||
"Sent! Scroll down to view your message.": "Выслано! Прокрутите вниз, чтобы посмотреть свое сообщение.",
|
||||
"Sent! Your message is outside your current narrow.": "Выслано! Ваше сообщение, в данный момент, вне области вашей текущей настройки фильтра.",
|
||||
"Sent! Your message was sent to a stream you have muted.": "Выслано! Ваше сообщение было послано в канал, который вы заглушили.",
|
||||
"Sent! Your message was sent to a topic you have muted.": "Выслано! Ваше сообщение было послано в тему, которую вы заглушили.",
|
||||
"Sent! Your recent message is outside the current search.": "Выслано! Ваше сообщение находится вне пределах вашего текущего поиска.",
|
||||
"Set a status message": "Установить статус",
|
||||
"Set yourself as active": "",
|
||||
"Set yourself as unavailable": "",
|
||||
"Set yourself as active": "Переключить свой статус на \"активен\"",
|
||||
"Set yourself as unavailable": "Переключить свой статус на \"недоступен\"",
|
||||
"Settings": "Настройки",
|
||||
"Setup": "Настройка",
|
||||
"Setup two factor authentication": "Настройка двухфакторной аутентификации",
|
||||
"Show API key": "",
|
||||
"Show API key": "Показать API ключ",
|
||||
"Show counts for starred messages": "Показывать счётчик помеченных сообщений",
|
||||
"Show fewer": "",
|
||||
"Show fewer": "Показать меньше",
|
||||
"Show more": "Показать еще",
|
||||
"Show previews of linked websites": "Показать предпросмотр сайтов",
|
||||
"Show previews of uploaded and linked images": "Показать предпросмотр загруженных изображений и ссылок на них",
|
||||
"Show starred message count": "",
|
||||
"Show starred message count": "Показать количество избранных сообщений",
|
||||
"Show/change your API key": "Показать/изменить ваш API-ключ",
|
||||
"Signup notifications stream changed!": "Канал уведомлений о регистрации изменен!",
|
||||
"Signup notifications stream disabled!": "Канал уведомлений о регистрации отключен!",
|
||||
@@ -525,7 +525,7 @@
|
||||
"Slack's outgoing webhooks": "Исходящие вебхуки Slack",
|
||||
"Sorry, the file was too large.": "Извините, файл слишком большой.",
|
||||
"Star": "Отметить",
|
||||
"Start public export": "",
|
||||
"Start public export": "Начать публичную выгрузку",
|
||||
"Stream": "Канал",
|
||||
"Stream color": "Цвет канала",
|
||||
"Stream created recently": "Недавно созданные каналы",
|
||||
@@ -544,33 +544,33 @@
|
||||
"Subscribed successfully!": "Подписан успешно!",
|
||||
"Subscriber count": "Количество подписчиков",
|
||||
"Subscribers": "Подписчики",
|
||||
"Sunday": "",
|
||||
"System bot": "",
|
||||
"Sunday": "Воскресенье",
|
||||
"System bot": "Системный бот",
|
||||
"Task already exists": "Задача уже существует",
|
||||
"Text": "Текст",
|
||||
"The export URL is not yet available... Check back soon.": "",
|
||||
"The export URL is not yet available... Check back soon.": "Ссылка выгрузки пока еще не доступна... Возвращайтесь через несколько минут.",
|
||||
"The recipient __recipient__ is not valid": "Получатель __recipient__ не является допустимым",
|
||||
"The recipients __recipients__ are not valid": "Получатели __recipients__ не являются допустимыми",
|
||||
"The stream description cannot contain newline characters.": "",
|
||||
"The stream description cannot contain newline characters.": "Описание канала не должна содержать перенос строки.",
|
||||
"The stream description has been updated!": "Описание канала обновлено!",
|
||||
"The stream has been renamed!": "Канал переименован!",
|
||||
"Their password will be cleared from our systems, and any bots they maintain will be disabled.": "Их пароли будут стерты в нашей системе, а все их боты будут отключены.",
|
||||
"There are no messages to reply to.": "Нет сообщений для ответа.",
|
||||
"These settings are explained in detail in the <a target=\"_blank\" href=\"/help/stream-permissions\">help center</a>.": "Эти настройки подробно освещены в <a target=\"_blank\" href=\"/help/stream-permissions\">центре помощи</a>.",
|
||||
"This action is permanent and cannot be undone. All users will permanently lose access to their Zulip accounts.": "Это действие является перманентным и не может быть отменено. Все пользователи потеряют доступ к своим учетным записям Zulip.",
|
||||
"This is a <i class=\"fa fa-globe\" aria-hidden=\"true\"></i> <b>web public stream</b>. Any member of the organization can join without an invitation and anyone on the internet can read the content published.": "",
|
||||
"This is a <i class=\"hash\" aria-hidden=\"true\"></i> <b>public stream</b>. Any member of the organization can join without an invitation.": "",
|
||||
"This is a <i class=\"fa fa-globe\" aria-hidden=\"true\"></i> <b>web public stream</b>. Any member of the organization can join without an invitation and anyone on the internet can read the content published.": "Этот канал является <i class=\"fa fa-globe\" aria-hidden=\"true\"></i> <b>открытым сетевым каналом</b>. Любой член организации может к нему присоединиться без приглашения, и любой пользователь интернета может читать все, что в нем опубликовано.",
|
||||
"This is a <i class=\"hash\" aria-hidden=\"true\"></i> <b>public stream</b>. Any member of the organization can join without an invitation.": "Этот канал является <i class=\"hash\" aria-hidden=\"true\"></i> <b>открытым каналом</b>. Любой член организации может к нему присоединиться без приглашения.",
|
||||
"This is a <span class=\"fa fa-lock\" aria-hidden=\"true\"></span> <b>private stream</b>. Only people who have been invited can access its content, but any member of the stream can invite others.": "Это <span class=\"fa fa-lock\" aria-hidden=\"true\"></span> <b>закрытый канал</b>. Только приглашенные люди имеют доступ к его содержанию, но любой участник канала может пригласить других.",
|
||||
"This is a private stream": "Это закрытый канал",
|
||||
"This organization is configured to restrict editing of message content to __minutes_to_edit__ minutes after it is sent.": "Согласно настройкам этой организации, редактировать сообщение можно только в течение __minutes_to_edit__ минут после отправки.",
|
||||
"This stream is reserved for <strong>announcements</strong>. <br /> Are you sure you want to message all <strong>__count__</strong> people in this stream?": "Этот канал зарезервирован для <strong>анонсов</strong>. <br /> Вы уверены, что хотите отправить сообщение всем <strong>__count__</strong> пользователям в этом канале?",
|
||||
"Thursday": "",
|
||||
"Time": "",
|
||||
"Time format": "",
|
||||
"Thursday": "Четверг",
|
||||
"Time": "Время",
|
||||
"Time format": "Формат времени",
|
||||
"Time settings": "Настройки времени",
|
||||
"Time zone": "Часовой пояс",
|
||||
"Time's up!": "Время вышло!",
|
||||
"Tip: You can also send \"/poll Some question\"": "",
|
||||
"Tip: You can also send \"/poll Some question\"": "Совет: вы также можете начать голосование через \"/poll какой-либо вопрос\"",
|
||||
"Today": "Сегодня",
|
||||
"Toggle subscription": "Переключить подписку",
|
||||
"Tomorrow": "Завтра",
|
||||
@@ -578,14 +578,14 @@
|
||||
"Topic editing only": "Доступно только редактирование темы",
|
||||
"Topic muted": "Тема заглушена",
|
||||
"Try again": "Попробуйте еще",
|
||||
"Tuesday": "",
|
||||
"Tuesday": "Вторник",
|
||||
"Two factor authentication": "Двухфакторная аутентификация",
|
||||
"Type": "Тип",
|
||||
"URL format string": "Строка формата URL",
|
||||
"URL pattern": "",
|
||||
"URL pattern": "Шаблон URL",
|
||||
"Un-collapse": "Развернуть",
|
||||
"Unable to upload that many files at once.": "Не могу загрузить столько много файлов за раз.",
|
||||
"Unavailable": "",
|
||||
"Unavailable": "Недоступно",
|
||||
"Uncheck all": "Снять отметки со всего",
|
||||
"Unknown": "Неизвестный",
|
||||
"Unless I say otherwise for a particular stream, I want:": "Если не указано иначе для конкретного канала, я хочу:",
|
||||
@@ -594,9 +594,9 @@
|
||||
"Unmute the topic <b>__topic__</b>": "Включить оповещения темы <b>__topic__</b>",
|
||||
"Unmute the topic <b>__topic_name__</b>": "Включить оповещения из темы <b>__topic_name__</b>",
|
||||
"Unpin stream <b>__stream.name__</b> from top": "Открепить канал <b>__stream.name__</b> сверху списка",
|
||||
"Unread count summary (appears in desktop sidebar and browser tab)": "",
|
||||
"Unread count summary (appears in desktop sidebar and browser tab)": "Кол-во непрочитанных (отображается в боковой панели на рабочем столе и во вкладке браузера)",
|
||||
"Unstar": "Снять отметку",
|
||||
"Unstar all messages": "",
|
||||
"Unstar all messages": "Снять отметку со всех отмеченных сообщений",
|
||||
"Unsubscribe": "Отписаться",
|
||||
"Unsubscribed successfully!": "Отписан успешно!",
|
||||
"Up to N minutes after posting": "До N минут после публикации",
|
||||
@@ -606,19 +606,19 @@
|
||||
"Upload image or GIF": "Загрузить изображение или GIF",
|
||||
"Upload logo": "Загрузить логотип",
|
||||
"Upload new logo": "Загрузить новый логотип",
|
||||
"Upload new profile picture": "",
|
||||
"Upload profile picture": "",
|
||||
"Uploading logo.": "",
|
||||
"Uploading profile picture.": "",
|
||||
"Upload new profile picture": "Загрузить новое фото профиля",
|
||||
"Upload profile picture": "Загрузить фото профиля",
|
||||
"Uploading logo.": "Загрузка логотипа",
|
||||
"Uploading profile picture.": "Загрузка фото профиля",
|
||||
"Uploading\u2026": "Загрузка\\u2026",
|
||||
"Use full width on wide screens": "",
|
||||
"Use full width on wide screens": "Использовать широкий формат отображения рабочей области",
|
||||
"User already subscribed.": "Пользователь уже подписан.",
|
||||
"User group added!": "Группа пользователей добавлена!",
|
||||
"User groups allow you to <a href=\"/help/mention-a-user-or-group\" target=\"_blank\">mention</a> multiple users at once. When you mention a user group, everyone in the group is notified as if they were individually mentioned.": "",
|
||||
"User groups allow you to <a href=\"/help/mention-a-user-or-group\" target=\"_blank\">mention</a> multiple users at once. When you mention a user group, everyone in the group is notified as if they were individually mentioned.": "Группы пользователей позволяют вам <a href=\"/help/mention-a-user-or-group\" target=\"_blank\">упоминать</a> несколько пользователей сразу. Если вы упоминаете группу пользователей, каждый, находящийся в данной группе, получает уведомление, будто упомянули лично его.",
|
||||
"User identity": "Идентификатор пользователя",
|
||||
"User is already not subscribed.": "Пользователь уже отписан.",
|
||||
"User is deactivated": "Пользователь отключен",
|
||||
"User list on left sidebar in narrow windows": "Список пользователей в левой боковой панели",
|
||||
"User list on left sidebar in narrow windows": "Список пользователей в левой боковой панели на узких окнах",
|
||||
"User role": "Роль пользователя",
|
||||
"User settings": "Настройки пользователя",
|
||||
"User(s) invited successfully.": "Пользователи успешно приглашены.",
|
||||
@@ -627,7 +627,7 @@
|
||||
"Video chat provider": "Провайдер видеочата",
|
||||
"View edit history": "Показать историю редактирования",
|
||||
"View file": "Показать файл",
|
||||
"View full profile": "",
|
||||
"View full profile": "Просмотр полного профиля",
|
||||
"View messages sent": "Просмотр отправленных сообщений",
|
||||
"View private messages": "Показать личные сообщения",
|
||||
"View private messages to myself": "Показать личные сообщения для меня",
|
||||
@@ -635,21 +635,21 @@
|
||||
"View source / Edit topic": "Исходный текст / Редактирование темы",
|
||||
"View stream": "Просмотр канала",
|
||||
"View your profile": "Отрыть профиль пользователя",
|
||||
"Visual desktop notifications": "Визуальные оповещения на рабочем столе",
|
||||
"Waiting period (days)": "",
|
||||
"Waiting period before new members turn into full members": "",
|
||||
"Visual desktop notifications": "Визуальные уведомления на рабочем столе",
|
||||
"Waiting period (days)": "Период ожидания (дни)",
|
||||
"Waiting period before new members turn into full members": "Период ожидания, пока из новых членов становятся полноценные члены",
|
||||
"Warning: <strong>__stream_name__</strong> is a private stream.": "Предупреждение: <strong> __ stream_name __ </ strong> - закрытый канал.",
|
||||
"We are about to have a poll. Please wait for the question.": "",
|
||||
"We recommend against deleting topics unless needed for security reasons or managing abuse. Deleted messages can be confusing for users who may later visit the topic via notifications.": "",
|
||||
"Wednesday": "",
|
||||
"Who can access user email addresses": "",
|
||||
"We are about to have a poll. Please wait for the question.": "У нас будет голосование. Пожалуйста, подождите, пока появится вопрос.",
|
||||
"We recommend against deleting topics unless needed for security reasons or managing abuse. Deleted messages can be confusing for users who may later visit the topic via notifications.": "Мы не советуем удалять темы, за исключением, если это необходимо по соображением безопасности или борьбы со злоупотреблением. Удаленные сообщения могут смущать пользователей, которые будут позже просматривать тему по уведомлениям.",
|
||||
"Wednesday": "Среда",
|
||||
"Who can access user email addresses": "Кто имеет доступ к адресам электронной почты пользователей",
|
||||
"Who can add bots": "Кто может добавлять ботов",
|
||||
"Who can add custom emoji": "Кто может добавлять эмодзи",
|
||||
"Who can add users to streams": "",
|
||||
"Who can create and manage user groups": "",
|
||||
"Who can add users to streams": "Кто имеет право добавлять пользователей к каналам",
|
||||
"Who can create and manage user groups": "Кто имеет право создавать и управлять группами пользователей",
|
||||
"Who can create streams": "Кто может создавать каналы",
|
||||
"Working\u2026": "Работаем\\u2026",
|
||||
"Would you like to unstar all starred messages? This action cannot be undone.": "",
|
||||
"Would you like to unstar all starred messages? This action cannot be undone.": "Вы хотите снять отмметку со всех отмеченных сообщений? Это действие нельзя будет отменить.",
|
||||
"Write": "Редактирование",
|
||||
"Yes, delete this stream": "Да, удалить этот канал",
|
||||
"Yes, send": "Да, отправить",
|
||||
@@ -661,9 +661,9 @@
|
||||
"You and __recipients__": "Вы и __recipients__",
|
||||
"You are not currently subscribed to this stream.": "Вы не подписаны на этот канал.",
|
||||
"You are not subscribed to stream __stream__": "Вы не подписаны на канал __stream__",
|
||||
"You are searching for messages that are sent by more than one person, which is not possible.": "",
|
||||
"You are searching for messages that belong to more than one stream, which is not possible.": "",
|
||||
"You are searching for messages that belong to more than one topic, which is not possible.": "",
|
||||
"You are searching for messages that are sent by more than one person, which is not possible.": "Вы ищете сообщения, которые были посланы более чем одним человеком, а это невозможно.",
|
||||
"You are searching for messages that belong to more than one stream, which is not possible.": "Вы ищете сообщения, которые относятся к более чем одному каналу, а это невозможно.",
|
||||
"You are searching for messages that belong to more than one topic, which is not possible.": "Вы ищете сообщения, которые относятся к более чем одной теме, а это невозможно.",
|
||||
"You cannot create a stream with no subscribers!": "Вы не можете создать канал без подписчиков!",
|
||||
"You have muted the topic <span class=\"topic\"></span> under the <span class=\"stream\"></span> stream.": "Вы заглушили тему <span class=\"topic\"></span> в канале <span class=\"stream\"></span>.",
|
||||
"You have no active bots.": "У вас нет активных ботов.",
|
||||
@@ -673,37 +673,37 @@
|
||||
"You have nothing to send!": "Нечего отправлять!",
|
||||
"You must be an organization administrator to create a stream without subscribing.": "Вы должны быть администратором, чтобы создавать каналы не подписываясь.",
|
||||
"You need to be running Zephyr mirroring in order to send messages!": "Зеркалирование Zephyr должно быть включено для возможности отправки сообщений!",
|
||||
"You searched for:": "",
|
||||
"You searched for:": "Вы искали:",
|
||||
"You subscribed to stream __stream__": "Вы подписаны на канал __stream__",
|
||||
"You unsubscribed from stream __stream__": "Вы отписаны от канала __stream__",
|
||||
"You're not subscribed to this stream. You will not be notified if other users reply to your message.": "Вы не подписаны на этот канал. Вы не получите уведомление если кто-то ответит на ваше сообщение.",
|
||||
"Your API key:": "Ваш API-ключ:",
|
||||
"Your reminder note is empty!": "Текст вашего напоминания пуст!",
|
||||
"Zoom API key (required)": "",
|
||||
"Zoom API secret (required if changed)": "",
|
||||
"Zoom user ID or email address (required)": "",
|
||||
"[Condense message]": "",
|
||||
"Zoom API key (required)": "API-ключ Zoom (необходим)",
|
||||
"Zoom API secret (required if changed)": "Секретный API-ключ Zoom (необходим, если изменился)",
|
||||
"Zoom user ID or email address (required)": "ID пользователя Zoom или адрес электронной почты (необходим)",
|
||||
"[Condense message]": "[Свернуть сообщение]",
|
||||
"[Configure]": "[Настроить]",
|
||||
"[Disable]": "[Отключить]",
|
||||
"[More...]": "[Еще...]",
|
||||
"__days__ days ago": "",
|
||||
"__days__ days ago": "__days__ дней тому назад",
|
||||
"__hours__ hours ago": "__hours__ часов назад",
|
||||
"__last_active_date__": "",
|
||||
"__last_active_date__": "__last_active_date__",
|
||||
"__minutes__ min to edit": "осталось __minutes__ мин. для изменения",
|
||||
"__minutes__ minutes ago": "__minutes__ минут назад",
|
||||
"__seconds__ sec to edit": "осталось __seconds__ сек. для изменения",
|
||||
"__starred_status__ this message": "__starred_status__ сообщение",
|
||||
"__wildcard_mention_token__ (Notify stream)": "__wildcard_mention_token__ (канал уведомлений)",
|
||||
"and": "и",
|
||||
"clear": "",
|
||||
"clear": "очистить",
|
||||
"cookie": "куки",
|
||||
"group private messages with __recipient__": "",
|
||||
"group private messages with __recipient__": "группировать личные сообщения с __recipient__",
|
||||
"in 1 hour": "через 1 час",
|
||||
"in 20 minutes": "через 20 минут",
|
||||
"in 3 hours": "через 3 часа",
|
||||
"leafy green vegetable": "листвяной зеленый овощ",
|
||||
"marketing": "маркетинг",
|
||||
"more topics": "еще темы",
|
||||
"private messages with __recipient__": "",
|
||||
"private messages with yourself": ""
|
||||
"private messages with __recipient__": "личные сообщения с __recipient__",
|
||||
"private messages with yourself": "личные сообщения с собой"
|
||||
}
|
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Zulip\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-12-13 04:30+0000\n"
|
||||
"POT-Creation-Date: 2020-01-16 19:35+0000\n"
|
||||
"PO-Revision-Date: 2018-04-11 21:06+0000\n"
|
||||
"Last-Translator: Tim Abbott <tabbott@kandralabs.com>\n"
|
||||
"Language-Team: Tamil (http://www.transifex.com/zulip/zulip/language/ta/)\n"
|
||||
@@ -18,7 +18,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: analytics/views.py:80 zerver/decorator.py:522 zerver/decorator.py:531
|
||||
#: analytics/views.py:80 zerver/decorator.py:525 zerver/decorator.py:534
|
||||
msgid "Not allowed for guest users"
|
||||
msgstr ""
|
||||
|
||||
@@ -1351,7 +1351,7 @@ msgstr ""
|
||||
msgid "Organization permissions"
|
||||
msgstr ""
|
||||
|
||||
#: templates/zerver/app/settings_overlay.html:72 zerver/models.py:1769
|
||||
#: templates/zerver/app/settings_overlay.html:72 zerver/models.py:1767
|
||||
msgid "Custom emoji"
|
||||
msgstr ""
|
||||
|
||||
@@ -2957,35 +2957,35 @@ msgstr ""
|
||||
msgid "Account is not associated with this subdomain"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:471 zerver/decorator.py:533
|
||||
#: zerver/decorator.py:474 zerver/decorator.py:536
|
||||
msgid "This endpoint does not accept bot requests."
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:513
|
||||
#: zerver/decorator.py:516
|
||||
msgid "Must be an server administrator"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:588
|
||||
#: zerver/decorator.py:591
|
||||
msgid "This endpoint requires HTTP basic authentication."
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:591
|
||||
#: zerver/decorator.py:594
|
||||
msgid "Invalid authorization header for basic auth"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:593
|
||||
#: zerver/decorator.py:596
|
||||
msgid "Missing authorization header for basic auth"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:670
|
||||
#: zerver/decorator.py:673
|
||||
msgid "Not logged in"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:681
|
||||
#: zerver/decorator.py:684
|
||||
msgid "Webhook bots can only access webhooks"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/decorator.py:740
|
||||
#: zerver/decorator.py:743
|
||||
msgid "Access denied"
|
||||
msgstr ""
|
||||
|
||||
@@ -3408,7 +3408,7 @@ msgstr ""
|
||||
msgid "Must be an organization administrator or emoji author"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/lib/emoji.py:109 zerver/models.py:601
|
||||
#: zerver/lib/emoji.py:109 zerver/models.py:599
|
||||
msgid "Invalid characters in emoji name"
|
||||
msgstr ""
|
||||
|
||||
@@ -3462,7 +3462,11 @@ msgstr ""
|
||||
msgid "Invalid API key"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/lib/exceptions.py:229
|
||||
#: zerver/lib/exceptions.py:222
|
||||
msgid "Malformed API key"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/lib/exceptions.py:234
|
||||
#, python-brace-format
|
||||
msgid ""
|
||||
"The '{event_type}' event isn't currently supported by the {webhook_name} "
|
||||
@@ -3910,73 +3914,73 @@ msgstr ""
|
||||
msgid "API usage exceeded rate limit"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:255
|
||||
#: zerver/models.py:253
|
||||
#, fuzzy
|
||||
#| msgid "Username"
|
||||
msgid "stream events"
|
||||
msgstr "பயனர்பெயர்"
|
||||
|
||||
#: zerver/models.py:274
|
||||
#: zerver/models.py:272
|
||||
msgid "Available on Zulip Standard. Upgrade to access."
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:664
|
||||
#: zerver/models.py:662
|
||||
#, python-format
|
||||
msgid "Invalid filter pattern. Valid characters are %s."
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:680
|
||||
#: zerver/models.py:678
|
||||
msgid "Invalid URL format string."
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:1768
|
||||
#: zerver/models.py:1766
|
||||
msgid "Unicode emoji"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:1770
|
||||
#: zerver/models.py:1768
|
||||
msgid "Zulip extra emoji"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2676
|
||||
#: zerver/models.py:2690
|
||||
#, python-format
|
||||
msgid "Invalid user ID: %d"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2680
|
||||
#: zerver/models.py:2694
|
||||
#, python-format
|
||||
msgid "User with ID %d is deactivated"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2683
|
||||
#: zerver/models.py:2697
|
||||
#, python-format
|
||||
msgid "User with ID %d is a bot"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2713
|
||||
#: zerver/models.py:2727
|
||||
msgid "List of options"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2716
|
||||
#: zerver/models.py:2730
|
||||
msgid "Person picker"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2728
|
||||
#: zerver/models.py:2742
|
||||
msgid "Short text"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2729
|
||||
#: zerver/models.py:2743
|
||||
msgid "Long text"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2730
|
||||
#: zerver/models.py:2744
|
||||
msgid "Date picker"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2731
|
||||
#: zerver/models.py:2745
|
||||
msgid "Link"
|
||||
msgstr ""
|
||||
|
||||
#: zerver/models.py:2732
|
||||
#: zerver/models.py:2746
|
||||
msgid "External account"
|
||||
msgstr ""
|
||||
|
||||
|
2
puppet/zulip/files/sasl2/memcached.conf
Normal file
2
puppet/zulip/files/sasl2/memcached.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
mech_list: plain
|
||||
sasldb_path: /etc/sasl2/memcached-sasldb2
|
@@ -3,6 +3,7 @@
|
||||
class zulip::app_frontend_base {
|
||||
include zulip::common
|
||||
include zulip::nginx
|
||||
include zulip::sasl_modules
|
||||
include zulip::supervisor
|
||||
|
||||
$web_packages = [
|
||||
@@ -149,4 +150,15 @@ class zulip::app_frontend_base {
|
||||
mode => '0755',
|
||||
source => 'puppet:///modules/zulip/nagios_plugins/zulip_app_frontend',
|
||||
}
|
||||
|
||||
if $::osfamily == 'debian' {
|
||||
# The pylibmc wheel looks for SASL plugins in the wrong place.
|
||||
file { '/usr/lib64':
|
||||
ensure => directory,
|
||||
}
|
||||
file { '/usr/lib64/sasl2':
|
||||
ensure => link,
|
||||
target => "/usr/lib/${::rubyplatform}/sasl2",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -76,7 +76,6 @@ class zulip::base {
|
||||
'embed_links',
|
||||
'embedded_bots',
|
||||
'error_reports',
|
||||
'feedback_messages',
|
||||
'invites',
|
||||
'missedmessage_email_senders',
|
||||
'email_senders',
|
||||
|
@@ -14,5 +14,9 @@ class zulip::camo {
|
||||
group => 'root',
|
||||
mode => '0644',
|
||||
content => template('zulip/camo_defaults.template.erb'),
|
||||
notify => Service[camo],
|
||||
}
|
||||
service { 'camo':
|
||||
ensure => running,
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,58 @@
|
||||
class zulip::memcached {
|
||||
$memcached_packages = ['memcached']
|
||||
include zulip::sasl_modules
|
||||
|
||||
$memcached_packages = $::osfamily ? {
|
||||
'debian' => [ 'memcached', 'sasl2-bin' ],
|
||||
'redhat' => [ 'memcached' ],
|
||||
}
|
||||
package { $memcached_packages: ensure => 'installed' }
|
||||
|
||||
$memcached_memory = zulipconf('memcached', 'memory', $zulip::base::total_memory_mb / 8)
|
||||
file { '/etc/sasl2':
|
||||
ensure => directory,
|
||||
}
|
||||
file { '/etc/sasl2/memcached-zulip-password':
|
||||
# We cache the password in this file so we can check whether it
|
||||
# changed and avoid running saslpasswd2 if it didn't.
|
||||
require => File['/etc/sasl2'],
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0600',
|
||||
content => zulipsecret('secrets', 'memcached_password', ''),
|
||||
notify => Exec[generate_memcached_sasldb2],
|
||||
}
|
||||
exec { 'generate_memcached_sasldb2':
|
||||
require => [
|
||||
Package[$memcached_packages],
|
||||
Package[$zulip::sasl_modules::sasl_module_packages],
|
||||
File['/etc/sasl2/memcached-zulip-password'],
|
||||
],
|
||||
refreshonly => true,
|
||||
# Pass the hostname explicitly because otherwise saslpasswd2
|
||||
# lowercases it and memcached does not.
|
||||
command => "bash -c 'saslpasswd2 -p -f /etc/sasl2/memcached-sasldb2 \
|
||||
-a memcached -u \"\$HOSTNAME\" zulip < /etc/sasl2/memcached-zulip-password'",
|
||||
}
|
||||
file { '/etc/sasl2/memcached-sasldb2':
|
||||
require => Exec[generate_memcached_sasldb2],
|
||||
owner => 'memcache',
|
||||
group => 'memcache',
|
||||
mode => '0600',
|
||||
}
|
||||
file { '/etc/sasl2/memcached.conf':
|
||||
require => File['/etc/sasl2'],
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0644',
|
||||
source => 'puppet:///modules/zulip/sasl2/memcached.conf',
|
||||
notify => Service[memcached],
|
||||
}
|
||||
file { '/etc/memcached.conf':
|
||||
ensure => file,
|
||||
require => Package[memcached],
|
||||
require => [
|
||||
Package[$memcached_packages],
|
||||
Package[$zulip::sasl_modules::sasl_module_packages]
|
||||
],
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0644',
|
||||
|
@@ -19,7 +19,7 @@ class zulip::redis {
|
||||
package { $redis_packages: ensure => 'installed' }
|
||||
|
||||
$file = "${redis_dir}/redis.conf"
|
||||
$zulip_redisconf = "${redis_dir}/zuli-redis.conf"
|
||||
$zulip_redisconf = "${redis_dir}/zulip-redis.conf"
|
||||
$line = "include ${zulip_redisconf}"
|
||||
exec { 'redis':
|
||||
unless => "/bin/grep -Fxqe '${line}' '${file}'",
|
||||
@@ -27,18 +27,32 @@ class zulip::redis {
|
||||
command => "bash -c \"(/bin/echo; /bin/echo '# Include Zulip-specific configuration'; /bin/echo '${line}') >> '${file}'\"",
|
||||
require => [Package[$redis],
|
||||
File[$zulip_redisconf],
|
||||
Exec['rediscleanup']],
|
||||
Exec['rediscleanup-zuli-redis']],
|
||||
}
|
||||
|
||||
exec { 'rediscleanup':
|
||||
onlyif => "echo '80a4cee76bac751576c3db8916fc50a6ea319428 ${file}' | sha1sum -c",
|
||||
command => "head -n-3 ${file} | sponge ${file}",
|
||||
# Fix the typo in the path to $zulip_redisconf introduced in
|
||||
# 071e32985c1207f20043e1cf28f82300d9f23f31 without triggering a
|
||||
# redis restart.
|
||||
$legacy_wrong_filename = "${redis_dir}/zuli-redis.conf"
|
||||
exec { 'rediscleanup-zuli-redis':
|
||||
onlyif => "test -e ${legacy_wrong_filename}",
|
||||
command => "
|
||||
mv ${legacy_wrong_filename} ${zulip_redisconf}
|
||||
perl -0777 -pe '
|
||||
if (m|^\\Q${line}\\E\$|m) {
|
||||
s|^\\n?(:?# Include Zulip-specific configuration\\n)?include \\Q${legacy_wrong_filename}\\E\\n||m;
|
||||
} else {
|
||||
s|^include \\Q${legacy_wrong_filename}\\E\$|${line}|m;
|
||||
}
|
||||
' -i /etc/redis/redis.conf
|
||||
",
|
||||
provider => shell,
|
||||
}
|
||||
|
||||
$redis_password = zulipsecret('secrets', 'redis_password', '')
|
||||
file { $zulip_redisconf:
|
||||
ensure => file,
|
||||
require => Package[$redis],
|
||||
require => [Package[$redis], Exec['rediscleanup-zuli-redis']],
|
||||
owner => 'redis',
|
||||
group => 'redis',
|
||||
mode => '0640',
|
||||
|
7
puppet/zulip/manifests/sasl_modules.pp
Normal file
7
puppet/zulip/manifests/sasl_modules.pp
Normal file
@@ -0,0 +1,7 @@
|
||||
class zulip::sasl_modules {
|
||||
$sasl_module_packages = $::osfamily ? {
|
||||
'debian' => [ 'libsasl2-modules' ],
|
||||
'redhat' => [ 'cyrus-sasl-plain' ],
|
||||
}
|
||||
package { $sasl_module_packages: ensure => 'installed' }
|
||||
}
|
@@ -27,7 +27,7 @@ logfile /var/log/memcached.log
|
||||
|
||||
# Run the daemon as root. The start-memcached will default to running as root if no
|
||||
# -u command is present in this config file
|
||||
-u nobody
|
||||
-u memcache
|
||||
|
||||
# Specify which IP address to listen on. The default is to listen on all IP addresses
|
||||
# This parameter is one of the only security measures that memcached has, so make sure
|
||||
@@ -50,3 +50,6 @@ logfile /var/log/memcached.log
|
||||
|
||||
# Maximize core file limit
|
||||
# -r
|
||||
|
||||
# Enable SASL authentication
|
||||
-S
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# This file is managed by puppet; local changes will be overridden.
|
||||
|
||||
smtpd_banner = $myhostname ESMTP $mail_name (Zulip Voyager)
|
||||
smtpd_banner = $myhostname ESMTP $mail_name (Zulip)
|
||||
biff = no
|
||||
|
||||
# appending .domain is the MUA's job.
|
||||
|
@@ -469,17 +469,6 @@ define service {
|
||||
contact_groups admins
|
||||
}
|
||||
|
||||
define service {
|
||||
use generic-service
|
||||
service_description Check rabbitmq feedback messages consumers
|
||||
check_command check_rabbitmq_consumers!feedback_messages
|
||||
# Workaround weird checks 40s after first error causing alerts
|
||||
# from a single failure because cron hasn't run again yet
|
||||
max_check_attempts 3
|
||||
hostgroup_name frontends
|
||||
contact_groups admins
|
||||
}
|
||||
|
||||
define service {
|
||||
use generic-service
|
||||
service_description Check rabbitmq message sender consumers
|
||||
@@ -589,7 +578,7 @@ define service {
|
||||
define service {
|
||||
use generic-service
|
||||
hostgroup_name staging_frontends
|
||||
service_description Check email deliverer process which is only used on Zulip Voyager
|
||||
service_description Check email deliverer process
|
||||
check_command check_email_deliverer_process
|
||||
contact_groups admins
|
||||
}
|
||||
@@ -597,7 +586,7 @@ define service {
|
||||
define service {
|
||||
use generic-service
|
||||
hostgroup_name staging_frontends
|
||||
service_description Check email deliverer backlog which is only used on Zulip Voyager
|
||||
service_description Check email deliverer backlog
|
||||
check_command check_email_deliverer_backlog
|
||||
contact_groups admins
|
||||
}
|
||||
|
@@ -219,7 +219,7 @@ cryptography==2.8 \
|
||||
--hash=sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2 \
|
||||
--hash=sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf \
|
||||
--hash=sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8 \
|
||||
# via apns2, moto, pyopenssl, requests, scrapy, service-identity, sshpubkeys
|
||||
# via apns2, moto, pyopenssl, requests, scrapy, service-identity, social-auth-core, sshpubkeys
|
||||
cssselect==1.1.0 \
|
||||
--hash=sha256:f612ee47b749c877ebae5bb77035d8f4202c6ad0f0fc1271b3c18ad6c4468ecf \
|
||||
--hash=sha256:f95f8dedd925fd8f54edb3d2dfb44c190d9d18512377d3c1e2388d16126879bc \
|
||||
@@ -757,10 +757,10 @@ social-auth-app-django==3.1.0 \
|
||||
--hash=sha256:6d0dd18c2d9e71ca545097d57b44d26f59e624a12833078e8e52f91baf849778 \
|
||||
--hash=sha256:9237e3d7b6f6f59494c3b02e0cce6efc69c9d33ad9d1a064e3b2318bcbe89ae3 \
|
||||
--hash=sha256:f151396e5b16e2eee12cd2e211004257826ece24fc4ae97a147df386c1cd7082
|
||||
social-auth-core==3.2.0 \
|
||||
--hash=sha256:47cd2458c8fefd02466b0c514643e02ad8b61d8b4b69f7573e80882e3a97b0f0 \
|
||||
--hash=sha256:8320666548a532eb158968eda542bbe1863682357c432d8c4e28034a7f1e3b58 \
|
||||
--hash=sha256:d81ed681e3c0722300b61a0792c5db5d21206793f95ca810f010c1cc931c8d89 \
|
||||
social-auth-core==3.3.2 \
|
||||
--hash=sha256:1ce0f672827465df416b7170536cf6ac2415158fe993acc227aec1ead5d429ee \
|
||||
--hash=sha256:3d04148d3f01d163cbf893d35250abe86e3e759203bd6f3036fdb85f89f85109 \
|
||||
--hash=sha256:6320ff4644eece77dd8cec7939361918e26a877fc282974071f9a8892fd6df7e \
|
||||
# via social-auth-app-django
|
||||
sockjs-tornado==1.0.6 \
|
||||
--hash=sha256:ec12b0c37723b0aac56610fb9b6aa68390720d0c9c2a10461df030c3a1d9af95
|
||||
|
@@ -139,7 +139,7 @@ cryptography==2.8 \
|
||||
--hash=sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2 \
|
||||
--hash=sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf \
|
||||
--hash=sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8 \
|
||||
# via apns2, pyopenssl, requests
|
||||
# via apns2, pyopenssl, requests, social-auth-core
|
||||
cssselect==1.1.0 \
|
||||
--hash=sha256:f612ee47b749c877ebae5bb77035d8f4202c6ad0f0fc1271b3c18ad6c4468ecf \
|
||||
--hash=sha256:f95f8dedd925fd8f54edb3d2dfb44c190d9d18512377d3c1e2388d16126879bc \
|
||||
@@ -505,10 +505,10 @@ social-auth-app-django==3.1.0 \
|
||||
--hash=sha256:6d0dd18c2d9e71ca545097d57b44d26f59e624a12833078e8e52f91baf849778 \
|
||||
--hash=sha256:9237e3d7b6f6f59494c3b02e0cce6efc69c9d33ad9d1a064e3b2318bcbe89ae3 \
|
||||
--hash=sha256:f151396e5b16e2eee12cd2e211004257826ece24fc4ae97a147df386c1cd7082
|
||||
social-auth-core==3.2.0 \
|
||||
--hash=sha256:47cd2458c8fefd02466b0c514643e02ad8b61d8b4b69f7573e80882e3a97b0f0 \
|
||||
--hash=sha256:8320666548a532eb158968eda542bbe1863682357c432d8c4e28034a7f1e3b58 \
|
||||
--hash=sha256:d81ed681e3c0722300b61a0792c5db5d21206793f95ca810f010c1cc931c8d89 \
|
||||
social-auth-core==3.3.2 \
|
||||
--hash=sha256:1ce0f672827465df416b7170536cf6ac2415158fe993acc227aec1ead5d429ee \
|
||||
--hash=sha256:3d04148d3f01d163cbf893d35250abe86e3e759203bd6f3036fdb85f89f85109 \
|
||||
--hash=sha256:6320ff4644eece77dd8cec7939361918e26a877fc282974071f9a8892fd6df7e \
|
||||
# via social-auth-app-django
|
||||
sockjs-tornado==1.0.6 \
|
||||
--hash=sha256:ec12b0c37723b0aac56610fb9b6aa68390720d0c9c2a10461df030c3a1d9af95
|
||||
|
@@ -1,31 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Forward messages sent to the configured email gateway to Zulip.
|
||||
"""Postfix implementation of the incoming email gateway's helper for
|
||||
forwarding emails into Zulip.
|
||||
|
||||
For zulip.com, messages to that address go to the Inbox of emailgateway@zulip.com.
|
||||
Zulip voyager configurations will differ.
|
||||
https://zulip.readthedocs.io/en/latest/production/settings.html#email-gateway
|
||||
|
||||
Messages meant for Zulip have a special recipient form of
|
||||
The email gateway supports two major modes of operation: An email
|
||||
server (using postfix) where the email address configured in
|
||||
EMAIL_GATEWAY_PATTERN delivers emails directly to Zulip (this) or a
|
||||
cron job that connects to an IMAP inbox (which receives the emails)
|
||||
periodically.
|
||||
|
||||
<stream name>+<regenerable stream token>@streams.zulip.com
|
||||
|
||||
This pattern is configurable via the EMAIL_GATEWAY_PATTERN settings.py
|
||||
variable.
|
||||
|
||||
Configure your MTA to execute this script on message
|
||||
receipt with the contents of the message piped to standard input. The
|
||||
script will queue the message for processing. In this mode of invocation,
|
||||
you should pass the destination email address in the ORIGINAL_RECIPIENT
|
||||
environment variable.
|
||||
Zulip's puppet configuration takes care of configuring postfix to
|
||||
execute this script when emails are received by postfix, piping the
|
||||
email content via standard input (and the destination email address in
|
||||
the ORIGINAL_RECIPIENT environment variable).
|
||||
|
||||
In Postfix, you can express that via an /etc/aliases entry like this:
|
||||
|/home/zulip/deployments/current/scripts/lib/email-mirror-postfix -r ${original_recipient}
|
||||
|
||||
To manage DoS issues, this script does very little work (just sending
|
||||
an HTTP request to queue the message for processing) to avoid
|
||||
importing expensive libraries.
|
||||
|
||||
Also you can use optional keys to configure the script and change default values:
|
||||
|
||||
-s SHARED_SECRET For adding shared secret key if it is not contained in
|
||||
"/etc/zulip/zulip-secrets.conf".
|
||||
"/etc/zulip/zulip-secrets.conf". This key is used to authenticate
|
||||
the HTTP requests made by this tool.
|
||||
|
||||
-d HOST Destination Zulip host for email uploading. Address must contain type of
|
||||
HTTP protocol, i.e "https://example.com". Default value: "https://127.0.0.1".
|
||||
@@ -36,6 +38,7 @@ Also you can use optional keys to configure the script and change default values
|
||||
self-signed certificates. Default value: False.
|
||||
|
||||
-t Disable sending request to the Zulip server. Default value: False.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
|
@@ -259,11 +259,26 @@ EOF
|
||||
fi
|
||||
) > /etc/zulip/zulip.conf
|
||||
|
||||
case ",$PUPPET_CLASSES," in
|
||||
*,zulip::voyager,* | *,zulip::dockervoyager,* | *,zulip::app_frontend,*)
|
||||
if [ -z "$NO_OVERWRITE_SETTINGS" ] || ! [ -e "/etc/zulip/settings.py" ]; then
|
||||
cp -a "$ZULIP_PATH"/zproject/prod_settings_template.py /etc/zulip/settings.py
|
||||
if [ -n "$EXTERNAL_HOST" ]; then
|
||||
sed -i "s/^EXTERNAL_HOST =.*/EXTERNAL_HOST = '$EXTERNAL_HOST'/" /etc/zulip/settings.py
|
||||
fi
|
||||
if [ -n "$ZULIP_ADMINISTRATOR" ]; then
|
||||
sed -i "s/^ZULIP_ADMINISTRATOR =.*/ZULIP_ADMINISTRATOR = '$ZULIP_ADMINISTRATOR'/" /etc/zulip/settings.py
|
||||
fi
|
||||
fi
|
||||
ln -nsf /etc/zulip/settings.py "$ZULIP_PATH"/zproject/prod_settings.py
|
||||
"$ZULIP_PATH"/scripts/setup/generate_secrets.py --production
|
||||
;;
|
||||
esac
|
||||
|
||||
"$ZULIP_PATH"/scripts/zulip-puppet-apply -f
|
||||
|
||||
# Detect which features were selected for the below
|
||||
set +e
|
||||
[ -e "/etc/init.d/camo" ]; has_camo=$?
|
||||
[ -e "/etc/init.d/nginx" ]; has_nginx=$?
|
||||
[ -e "/etc/supervisor/conf.d/zulip.conf" ]; has_appserver=$?
|
||||
[ -e "/etc/cron.d/rabbitmq-numconsumers" ]; has_rabbit=$?
|
||||
@@ -272,7 +287,6 @@ set -e
|
||||
|
||||
# Docker service setup is done in the docker config, not here
|
||||
if [ "$DEPLOYMENT_TYPE" = "dockervoyager" ]; then
|
||||
has_camo=1
|
||||
has_nginx=1
|
||||
has_appserver=0
|
||||
has_rabbit=1
|
||||
@@ -280,7 +294,7 @@ if [ "$DEPLOYMENT_TYPE" = "dockervoyager" ]; then
|
||||
fi
|
||||
|
||||
if [ -n "$POSTGRES_MISSING_DICTIONARIES" ]; then
|
||||
export POSTGRES_MISSING_DICTIONARIES="true"
|
||||
crudini --set /etc/zulip/zulip.conf postgresql missing_dictionaries true
|
||||
fi
|
||||
|
||||
if [ -n "$REMOTE_POSTGRES" ]; then
|
||||
@@ -310,29 +324,6 @@ EOF
|
||||
service nginx restart
|
||||
fi
|
||||
|
||||
if [ "$has_appserver" = 0 ]; then
|
||||
"$ZULIP_PATH"/scripts/setup/generate_secrets.py --production
|
||||
if [ -z "$NO_OVERWRITE_SETTINGS" ] || ! [ -e "/etc/zulip/settings.py" ]; then
|
||||
cp -a "$ZULIP_PATH"/zproject/prod_settings_template.py /etc/zulip/settings.py
|
||||
if [ -n "$EXTERNAL_HOST" ]; then
|
||||
sed -i "s/^EXTERNAL_HOST =.*/EXTERNAL_HOST = '$EXTERNAL_HOST'/" /etc/zulip/settings.py
|
||||
fi
|
||||
if [ -n "$ZULIP_ADMINISTRATOR" ]; then
|
||||
sed -i "s/^ZULIP_ADMINISTRATOR =.*/ZULIP_ADMINISTRATOR = '$ZULIP_ADMINISTRATOR'/" /etc/zulip/settings.py
|
||||
fi
|
||||
fi
|
||||
ln -nsf /etc/zulip/settings.py "$ZULIP_PATH"/zproject/prod_settings.py
|
||||
fi
|
||||
|
||||
# Restart camo since generate_secrets.py likely replaced its secret key
|
||||
if [ "$has_camo" = 0 ]; then
|
||||
# Cut off stdin because a bug in the Debian packaging for camo
|
||||
# causes our stdin to leak to the daemon, which can cause tools
|
||||
# invoking the installer to hang.
|
||||
# TODO: fix in Debian too.
|
||||
service camo restart </dev/null
|
||||
fi
|
||||
|
||||
if [ "$has_rabbit" = 0 ]; then
|
||||
if ! rabbitmqctl status >/dev/null; then
|
||||
set +x
|
||||
|
@@ -47,13 +47,12 @@ consumers = defaultdict(int) # type: Dict[str, int]
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
|
||||
queues = {
|
||||
'deferred_work'
|
||||
'deferred_work',
|
||||
'digest_emails',
|
||||
'email_mirror',
|
||||
'embed_links',
|
||||
'embedded_bots',
|
||||
'error_reports',
|
||||
'feedback_messages',
|
||||
'invites',
|
||||
'message_sender',
|
||||
'missedmessage_emails',
|
||||
@@ -63,7 +62,7 @@ queues = {
|
||||
'outgoing_webhooks',
|
||||
'signups',
|
||||
'slow_queries',
|
||||
'user_activity'
|
||||
'user_activity',
|
||||
'user_activity_interval',
|
||||
'user_presence',
|
||||
# These queues may not be present if settings.TORNADO_PROCESSES > 1
|
||||
|
@@ -1,4 +1,10 @@
|
||||
CREATE USER zulip;
|
||||
\connect postgres
|
||||
DROP DATABASE IF EXISTS zulip;
|
||||
DO $$BEGIN
|
||||
CREATE USER zulip;
|
||||
EXCEPTION WHEN duplicate_object THEN
|
||||
RAISE NOTICE 'zulip user already exists';
|
||||
END$$;
|
||||
ALTER ROLE zulip SET search_path TO zulip,public;
|
||||
CREATE DATABASE zulip OWNER=zulip;
|
||||
\connect zulip
|
||||
|
@@ -1,26 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
BASE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../..")
|
||||
sys.path.append(BASE_DIR)
|
||||
|
||||
import scripts.lib.setup_path_on_import
|
||||
from zproject import settings
|
||||
import pylibmc
|
||||
|
||||
url = urlsplit("//" + settings.MEMCACHED_LOCATION)
|
||||
assert url.port is not None
|
||||
|
||||
print("Flushing memcached...")
|
||||
with socket.create_connection((url.hostname, url.port)) as f:
|
||||
f.sendall(b"flush_all\r\n")
|
||||
response = b""
|
||||
while b"\n" not in response:
|
||||
response += f.recv(4096)
|
||||
if response != b"OK\r\n":
|
||||
print(response, file=sys.stderr)
|
||||
print("Failed to flush memcached", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
pylibmc.Client(
|
||||
[settings.MEMCACHED_LOCATION],
|
||||
binary=True,
|
||||
username=settings.MEMCACHED_USERNAME,
|
||||
password=settings.MEMCACHED_PASSWORD,
|
||||
behaviors=settings.CACHES["default"]["OPTIONS"] # type: ignore # settings not typed properly
|
||||
).flush_all()
|
||||
|
@@ -17,11 +17,10 @@ import argparse
|
||||
import uuid
|
||||
import configparser
|
||||
from zerver.lib.utils import generate_random_token
|
||||
from zproject import settings
|
||||
|
||||
os.chdir(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
CAMO_CONFIG_FILENAME = '/etc/default/camo'
|
||||
|
||||
# Standard, 64-bit tokens
|
||||
AUTOGENERATED_SETTINGS = [
|
||||
'avatar_salt',
|
||||
@@ -30,18 +29,6 @@ AUTOGENERATED_SETTINGS = [
|
||||
'thumbor_key',
|
||||
]
|
||||
|
||||
# TODO: We can eliminate this function if we refactor the install
|
||||
# script to run generate_secrets before zulip-puppet-apply.
|
||||
def generate_camo_config_file(camo_key):
|
||||
# type: (str) -> None
|
||||
camo_config = """ENABLED=yes
|
||||
PORT=9292
|
||||
CAMO_KEY=%s
|
||||
""" % (camo_key,)
|
||||
with open(CAMO_CONFIG_FILENAME, 'w') as camo_file:
|
||||
camo_file.write(camo_config)
|
||||
print("Generated Camo config file %s" % (CAMO_CONFIG_FILENAME,))
|
||||
|
||||
def generate_django_secretkey():
|
||||
# type: () -> str
|
||||
"""Secret key generation taken from Django's startproject.py"""
|
||||
@@ -89,11 +76,52 @@ def generate_secrets(development=False):
|
||||
add_secret("local_database_password", generate_random_token(64))
|
||||
|
||||
if need_secret('secret_key'):
|
||||
add_secret('secret_key', generate_django_secretkey())
|
||||
secret_key = generate_django_secretkey()
|
||||
add_secret('secret_key', secret_key)
|
||||
# To prevent Django ImproperlyConfigured error
|
||||
settings.SECRET_KEY = secret_key
|
||||
|
||||
if need_secret('camo_key'):
|
||||
add_secret('camo_key', get_random_string(64))
|
||||
|
||||
if (
|
||||
not development
|
||||
and settings.MEMCACHED_LOCATION == "127.0.0.1:11211"
|
||||
and need_secret("memcached_password")
|
||||
):
|
||||
add_secret("memcached_password", generate_random_token(64))
|
||||
|
||||
if (
|
||||
not development
|
||||
and settings.REDIS_HOST == "127.0.0.1"
|
||||
and need_secret("redis_password")
|
||||
):
|
||||
# To prevent Puppet from restarting Redis, which would lose
|
||||
# data because we configured Redis to disable persistence, set
|
||||
# the Redis password on the running server and edit the config
|
||||
# file directly.
|
||||
|
||||
import redis
|
||||
from zerver.lib.redis_utils import get_redis_client
|
||||
|
||||
redis_password = generate_random_token(64)
|
||||
|
||||
for filename in ["/etc/redis/zuli-redis.conf", "/etc/redis/zulip-redis.conf"]:
|
||||
if os.path.exists(filename):
|
||||
with open(filename, "a") as f:
|
||||
f.write(
|
||||
"# Set a Redis password based on zulip-secrets.conf\n"
|
||||
"requirepass '%s'\n" % (redis_password,)
|
||||
)
|
||||
break
|
||||
|
||||
try:
|
||||
get_redis_client().config_set("requirepass", redis_password)
|
||||
except redis.exceptions.ConnectionError:
|
||||
pass
|
||||
|
||||
add_secret("redis_password", redis_password)
|
||||
|
||||
# zulip_org_key is generated using os.urandom().
|
||||
# zulip_org_id does not require a secure CPRNG,
|
||||
# it only needs to be unique.
|
||||
@@ -102,10 +130,6 @@ def generate_secrets(development=False):
|
||||
if need_secret('zulip_org_id'):
|
||||
add_secret('zulip_org_id', str(uuid.uuid4()))
|
||||
|
||||
if not development:
|
||||
# Write the Camo config file directly
|
||||
generate_camo_config_file(current_conf['camo_key'])
|
||||
|
||||
if len(lines) == 0:
|
||||
print("generate_secrets: No new secrets to generate.")
|
||||
return
|
||||
|
@@ -32,17 +32,6 @@ cd "$THIS_DIR/../.."
|
||||
./manage.py migrate --noinput
|
||||
./manage.py createcachetable third_party_api_results
|
||||
|
||||
if ! ./manage.py initialize_voyager_db; then
|
||||
set +x
|
||||
echo
|
||||
echo -e '\033[32mPopulating default database failed.'
|
||||
echo "After you fix the problem, you will need to do the following before rerunning this:"
|
||||
echo " * supervisorctl stop all # to stop all services that might be accessing the database"
|
||||
echo " * scripts/setup/postgres-init-db # run as root to drop and re-create the database"
|
||||
echo -e '\033[0m'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if the supervisor socket exists. If not, it could be:
|
||||
#
|
||||
# A) A normal installation went bad (supervisor hasn't started)
|
||||
|
@@ -44,10 +44,6 @@ source "$(dirname "$0")/terminate-psql-sessions" postgres zulip zulip_base
|
||||
# Make sure the current working directory is readable by postgres
|
||||
cd /
|
||||
|
||||
su "$POSTGRES_USER" -c 'psql -v ON_ERROR_STOP=1 -e' <<EOF
|
||||
DROP DATABASE IF EXISTS zulip;
|
||||
EOF
|
||||
|
||||
su "$POSTGRES_USER" -c 'psql -v ON_ERROR_STOP=1 -e' < "$(dirname "$0")/create-db.sql"
|
||||
)
|
||||
|
||||
|
@@ -100,6 +100,13 @@ def restore_backup(tarball_file):
|
||||
run(as_postgres + ["createdb", "-O", "zulip", "-T", "template0", "--", db_name])
|
||||
|
||||
if settings.PRODUCTION:
|
||||
# In case we are restoring a backup from an older Zulip
|
||||
# version, there may be new secrets to generate.
|
||||
subprocess.check_call([
|
||||
os.path.join(settings.DEPLOY_ROOT, "scripts", "setup", "generate_secrets.py"),
|
||||
"--production",
|
||||
])
|
||||
|
||||
# If there is a local rabbitmq, we need to reconfigure it
|
||||
# to ensure the rabbitmq password matches the value in the
|
||||
# restored zulip-secrets.conf. We need to be careful to
|
||||
|
BIN
static/images/integrations/alertmanager/001.png
Normal file
BIN
static/images/integrations/alertmanager/001.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
1
static/images/integrations/logos/prometheus.svg
Normal file
1
static/images/integrations/logos/prometheus.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg height="64" viewBox="-4.649 -0.667 64 64" width="64" xmlns="http://www.w3.org/2000/svg"><path d="M27.35-.667c-17.673 0-32 14.326-32 32s14.328 32 32 32 32-14.327 32-32-14.328-32-32-32zm0 59.89c-5.028 0-9.105-3.36-9.105-7.5h18.2c0 4.142-4.077 7.5-9.105 7.5zM42.4 49.24H12.31v-5.454H42.4zm-.108-8.26H12.397l-.297-.344c-3.08-3.74-3.804-5.7-4.508-7.68-.012-.066 3.734.766 6.39 1.363 0 0 1.367.316 3.364.68-1.918-2.25-3.057-5.107-3.057-8.03 0-6.415 4.92-12.02 3.145-16.55 1.728.14 3.575 3.646 3.7 9.126 1.837-2.538 2.605-7.172 2.605-10.014 0-2.942 1.94-6.36 3.878-6.477-1.73 2.85.448 5.29 2.382 11.35.726 2.276.633 6.106 1.193 8.535.186-5.045 1.053-12.405 4.254-14.946-1.412 3.2.21 7.205 1.318 9.13 1.79 3.106 2.873 5.46 2.873 9.9 0 2.984-1.102 5.793-2.96 8 2.113-.397 3.572-.754 3.572-.754l6.862-1.34c0-.001-.997 4.1-4.828 8.05z" fill="#da4e31"/></svg>
|
After Width: | Height: | Size: 852 B |
@@ -679,27 +679,6 @@ exports.initialize = function () {
|
||||
stream_list.toggle_filter_displayed(e);
|
||||
});
|
||||
|
||||
|
||||
// FEEDBACK
|
||||
|
||||
// Keep these 2 feedback bot triggers separate because they have to
|
||||
// propagate the event differently.
|
||||
$('.feedback').click(function () {
|
||||
compose_actions.start('private', {
|
||||
private_message_recipient: 'feedback@zulip.com',
|
||||
trigger: 'feedback menu item'});
|
||||
|
||||
});
|
||||
$('#feedback_button').click(function (e) {
|
||||
e.stopPropagation();
|
||||
popovers.hide_all();
|
||||
compose_actions.start('private', {
|
||||
private_message_recipient: 'feedback@zulip.com',
|
||||
trigger: 'feedback button'});
|
||||
|
||||
});
|
||||
|
||||
|
||||
// WEBATHENA
|
||||
|
||||
$('body').on('click', '.webathena_login', function (e) {
|
||||
|
@@ -736,7 +736,7 @@ exports.render_and_show_preview = function (preview_spinner, preview_content_box
|
||||
rendered_preview_html = rendered_content;
|
||||
}
|
||||
|
||||
preview_content_box.html(rendered_preview_html);
|
||||
preview_content_box.html(util.clean_user_content_links(rendered_preview_html));
|
||||
if (page_params.emojiset === "text") {
|
||||
preview_content_box.find(".emoji").replaceWith(function () {
|
||||
const text = $(this).attr("title");
|
||||
|
@@ -152,7 +152,7 @@ function Filter(operators) {
|
||||
if (operators === undefined) {
|
||||
this._operators = [];
|
||||
} else {
|
||||
this._operators = this._canonicalize_operators(operators);
|
||||
this._operators = this.fix_operators(operators);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,13 +382,42 @@ Filter.prototype = {
|
||||
},
|
||||
|
||||
can_mark_messages_read: function () {
|
||||
return _.every(this._operators, function (elem) {
|
||||
return (_.contains(['stream', 'topic', 'pm-with'], elem.operator)
|
||||
|| elem.operator === 'is' && elem.operand === 'private'
|
||||
|| elem.operator === 'in' && elem.operand === 'all'
|
||||
|| elem.operator === 'in' && elem.operand === 'home')
|
||||
&& !elem.negated;
|
||||
});
|
||||
const term_types = this.sorted_term_types();
|
||||
|
||||
if (_.isEqual(term_types, ['stream', 'topic'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_.isEqual(term_types, ['pm-with'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Some users really hate it when Zulip marks messages as read
|
||||
// in interleaved views, so we will eventually have a setting
|
||||
// that early-exits before the subsequent checks.
|
||||
|
||||
if (_.isEqual(term_types, ['stream'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_.isEqual(term_types, ['is-private'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_.isEqual(term_types, ['is-mentioned'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_.isEqual(term_types, [])) {
|
||||
// All view
|
||||
return true;
|
||||
}
|
||||
|
||||
if (term_types.length === 1 && _.contains(['in-home', 'in-all'], term_types[0])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
allow_use_first_unread_when_narrowing: function () {
|
||||
@@ -429,6 +458,26 @@ Filter.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
fix_operators: function (operators) {
|
||||
operators = this._canonicalize_operators(operators);
|
||||
operators = this._fix_redundant_is_private(operators);
|
||||
return operators;
|
||||
},
|
||||
|
||||
_fix_redundant_is_private: function (terms) {
|
||||
const is_pm_with = (term) => {
|
||||
return Filter.term_type(term) === 'pm-with';
|
||||
};
|
||||
|
||||
if (!_.any(terms, is_pm_with)) {
|
||||
return terms;
|
||||
}
|
||||
|
||||
return _.reject(terms, (term) => {
|
||||
return Filter.term_type(term) === 'is-private';
|
||||
});
|
||||
},
|
||||
|
||||
_canonicalize_operators: function (operators_mixed_case) {
|
||||
return _.map(operators_mixed_case, function (tuple) {
|
||||
return Filter.canonicalize_term(tuple);
|
||||
@@ -457,37 +506,6 @@ Filter.prototype = {
|
||||
return sorted_terms;
|
||||
},
|
||||
|
||||
is_exactly: function () {
|
||||
// TODO: in ES6 use spread operator
|
||||
//
|
||||
// Examples calls:
|
||||
// filter.is_exactly('stream', 'topic')
|
||||
// filter.is_exactly('pm-with')
|
||||
const wanted_term_types = [].slice.call(arguments);
|
||||
const term_types = this.sorted_term_types();
|
||||
|
||||
return _.isEqual(term_types, wanted_term_types);
|
||||
},
|
||||
|
||||
is_reading_mode: function () {
|
||||
// We only turn on "reading mode" for filters that
|
||||
// have contiguous messages for a narrow, as opposed
|
||||
// to "random access" queries like search:<keyword>
|
||||
// or id:<number> that jump you to parts of the message
|
||||
// view where you might only care about reading the
|
||||
// current message.
|
||||
const term_types = this.sorted_term_types();
|
||||
const wanted_list = [
|
||||
['stream'],
|
||||
['stream', 'topic'],
|
||||
['is-private'],
|
||||
['pm-with'],
|
||||
];
|
||||
return _.any(wanted_list, function (wanted_types) {
|
||||
return _.isEqual(wanted_types, term_types);
|
||||
});
|
||||
},
|
||||
|
||||
can_bucket_by: function () {
|
||||
// TODO: in ES6 use spread operator
|
||||
//
|
||||
@@ -573,7 +591,7 @@ Filter.term_type = function (term) {
|
||||
|
||||
result += operator;
|
||||
|
||||
if (_.contains(['is', 'has'], operator)) {
|
||||
if (_.contains(['is', 'has', 'in', 'streams'], operator)) {
|
||||
result += '-' + operand;
|
||||
}
|
||||
|
||||
@@ -582,6 +600,7 @@ Filter.term_type = function (term) {
|
||||
|
||||
Filter.sorted_term_types = function (term_types) {
|
||||
const levels = [
|
||||
'in',
|
||||
'streams',
|
||||
'stream', 'topic',
|
||||
'pm-with', 'group-pm-with', 'sender',
|
||||
|
@@ -590,7 +590,13 @@ MessageListView.prototype = {
|
||||
if (stream_id && !$(this).find(".highlight").length) {
|
||||
// Display the current name for stream if it is not
|
||||
// being displayed in search highlight.
|
||||
$(this).text("#" + stream_data.maybe_get_stream_name(stream_id));
|
||||
const stream_name = stream_data.maybe_get_stream_name(stream_id);
|
||||
if (stream_name !== undefined) {
|
||||
// If the stream has been deleted,
|
||||
// stream_data.maybe_get_stream_name might return
|
||||
// undefined. Otherwise, display the current stream name.
|
||||
$(this).text("#" + stream_name);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -601,7 +607,13 @@ MessageListView.prototype = {
|
||||
// being displayed in search highlight.
|
||||
const text = $(this).text();
|
||||
const topic = text.split('>', 2)[1];
|
||||
$(this).text("#" + stream_data.maybe_get_stream_name(stream_id) + ' > ' + topic);
|
||||
const stream_name = stream_data.maybe_get_stream_name(stream_id);
|
||||
if (stream_name !== undefined) {
|
||||
// If the stream has been deleted,
|
||||
// stream_data.maybe_get_stream_name might return
|
||||
// undefined. Otherwise, display the current stream name.
|
||||
$(this).text("#" + stream_name + ' > ' + topic);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -54,9 +54,7 @@ exports.process_message_for_recent_private_messages = function (message) {
|
||||
pm_conversations.set_partner(user_id);
|
||||
});
|
||||
|
||||
const user_ids_string = user_ids.join(',');
|
||||
|
||||
pm_conversations.recent.insert(user_ids_string, message.id);
|
||||
pm_conversations.recent.insert(user_ids, message.id);
|
||||
};
|
||||
|
||||
exports.set_message_booleans = function (message) {
|
||||
|
@@ -18,14 +18,6 @@ exports.filter = function () {
|
||||
return current_filter;
|
||||
};
|
||||
|
||||
exports.is_reading_mode = function () {
|
||||
if (current_filter === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return current_filter.is_reading_mode();
|
||||
};
|
||||
|
||||
exports.operators = function () {
|
||||
if (current_filter === undefined) {
|
||||
return new Filter(page_params.narrow).operators();
|
||||
|
@@ -19,7 +19,9 @@ const get_step = function ($process) {
|
||||
exports.initialize = function () {
|
||||
// if email has not been set up and the user is the admin, display a warning
|
||||
// to tell them to set up an email server.
|
||||
if (page_params.warn_no_email === true && page_params.is_admin) {
|
||||
if (page_params.insecure_desktop_app) {
|
||||
exports.open($("[data-process='insecure-desktop-app']"));
|
||||
} else if (page_params.warn_no_email === true && page_params.is_admin) {
|
||||
exports.open($("[data-process='email-server']"));
|
||||
} else {
|
||||
exports.open($("[data-process='notifications']"));
|
||||
|
@@ -18,13 +18,14 @@ exports.recent = (function () {
|
||||
const recent_message_ids = new Dict({fold_case: true}); // key is user_ids_string
|
||||
const recent_private_messages = [];
|
||||
|
||||
self.insert = function (user_ids_string, message_id) {
|
||||
if (user_ids_string === '') {
|
||||
// The API uses '' for self-PMs; convert it to the string
|
||||
// containing the current user's ID, which is the format
|
||||
// the webapp expects.
|
||||
user_ids_string = people.my_current_user_id().toString();
|
||||
self.insert = function (user_ids, message_id) {
|
||||
if (user_ids.length === 0) {
|
||||
// The server sends [] for self-PMs.
|
||||
user_ids = [people.my_current_user_id()];
|
||||
}
|
||||
user_ids.sort((a, b) => a - b);
|
||||
|
||||
const user_ids_string = user_ids.join(',');
|
||||
let conversation = recent_message_ids.get(user_ids_string);
|
||||
|
||||
if (conversation === undefined) {
|
||||
@@ -71,8 +72,7 @@ exports.recent = (function () {
|
||||
|
||||
self.initialize = function () {
|
||||
_.each(page_params.recent_private_conversations, function (conversation) {
|
||||
const user_ids_string = conversation.user_ids.join(",");
|
||||
self.insert(user_ids_string, conversation.max_message_id);
|
||||
self.insert(conversation.user_ids, conversation.max_message_id);
|
||||
});
|
||||
delete page_params.recent_private_messages;
|
||||
};
|
||||
|
@@ -70,7 +70,6 @@ function get_new_heights() {
|
||||
|
||||
const usable_height = viewport_height
|
||||
- parseInt($("#right-sidebar").css("marginTop"), 10)
|
||||
- $("#feedback_section").safeOuterHeight(true)
|
||||
- parseInt(buddy_list_wrapper.css("marginTop"), 10)
|
||||
- parseInt(buddy_list_wrapper.css("marginBottom"), 10)
|
||||
- $("#userlist-header").safeOuterHeight(true)
|
||||
|
@@ -312,8 +312,12 @@ exports.set_up = function () {
|
||||
});
|
||||
|
||||
$("#download_zuliprc").on("click", function () {
|
||||
const data = settings_bots.generate_zuliprc_content(people.my_current_email(),
|
||||
$("#api_key_value").text());
|
||||
const bot_object = {
|
||||
user_id: people.my_current_user_id(),
|
||||
email: people.my_current_email(),
|
||||
api_key: $("#api_key_value").text(),
|
||||
};
|
||||
const data = settings_bots.generate_zuliprc_content(bot_object);
|
||||
$(this).attr("href", settings_bots.encode_zuliprc_as_uri(data));
|
||||
});
|
||||
});
|
||||
|
@@ -115,13 +115,7 @@ exports.render_bots = function () {
|
||||
|
||||
exports.generate_zuliprc_uri = function (bot_id) {
|
||||
const bot = bot_data.get(bot_id);
|
||||
let token;
|
||||
// For outgoing webhooks, include the token in the zuliprc.
|
||||
// It's needed for authenticating to the Botserver.
|
||||
if (bot.bot_type === 3) {
|
||||
token = bot_data.get_services(bot_id)[0].token;
|
||||
}
|
||||
const data = exports.generate_zuliprc_content(bot.email, bot.api_key, token);
|
||||
const data = exports.generate_zuliprc_content(bot);
|
||||
return exports.encode_zuliprc_as_uri(data);
|
||||
};
|
||||
|
||||
@@ -129,10 +123,16 @@ exports.encode_zuliprc_as_uri = function (zuliprc) {
|
||||
return "data:application/octet-stream;charset=utf-8," + encodeURIComponent(zuliprc);
|
||||
};
|
||||
|
||||
exports.generate_zuliprc_content = function (email, api_key, token) {
|
||||
exports.generate_zuliprc_content = function (bot) {
|
||||
let token;
|
||||
// For outgoing webhooks, include the token in the zuliprc.
|
||||
// It's needed for authenticating to the Botserver.
|
||||
if (bot.bot_type === 3) {
|
||||
token = bot_data.get_services(bot.user_id)[0].token;
|
||||
}
|
||||
return "[api]" +
|
||||
"\nemail=" + email +
|
||||
"\nkey=" + api_key +
|
||||
"\nemail=" + bot.email +
|
||||
"\nkey=" + bot.api_key +
|
||||
"\nsite=" + page_params.realm_uri +
|
||||
(token === undefined ? "" : "\ntoken=" + token) +
|
||||
// Some tools would not work in files without a trailing new line.
|
||||
@@ -491,10 +491,10 @@ exports.set_up = function () {
|
||||
|
||||
new ClipboardJS('#copy_zuliprc', {
|
||||
text: function (trigger) {
|
||||
const bot_info = trigger.closest(".bot-information-box");
|
||||
const email = $(bot_info).find(".email .value").text();
|
||||
const api_key = $(bot_info).find(".api_key .api-key-value-and-button .value").text();
|
||||
const data = exports.generate_zuliprc_content(email.trim(), api_key.trim());
|
||||
const bot_info = $(trigger).closest(".bot-information-box").find(".bot_info");
|
||||
const bot_id = parseInt(bot_info.attr("data-user-id"), 10);
|
||||
const bot = bot_data.get(bot_id);
|
||||
const data = exports.generate_zuliprc_content(bot);
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
@@ -137,6 +137,10 @@ exports.set_up = function () {
|
||||
});
|
||||
$(".emojiset_choice").click(function () {
|
||||
const data = {emojiset: JSON.stringify($(this).val())};
|
||||
const current_emojiset = JSON.stringify(page_params.emojiset);
|
||||
if (current_emojiset === data.emojiset) {
|
||||
return;
|
||||
}
|
||||
const spinner = $("#emoji-settings-status").expectOne();
|
||||
loading.make_indicator(spinner, {text: settings_ui.strings.saving });
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
const util = require("./util");
|
||||
const render_settings_deactivation_stream_modal = require("../templates/settings/deactivation_stream_modal.hbs");
|
||||
const render_stream_member_list_entry = require('../templates/stream_member_list_entry.hbs');
|
||||
const render_subscription_settings = require('../templates/subscription_settings.hbs');
|
||||
@@ -111,7 +112,9 @@ exports.update_stream_name = function (sub, new_name) {
|
||||
exports.update_stream_description = function (sub) {
|
||||
const stream_settings = exports.settings_for_sub(sub);
|
||||
stream_settings.find('input.description').val(sub.description);
|
||||
stream_settings.find('.stream-description-editable').html(sub.rendered_description);
|
||||
stream_settings.find('.stream-description-editable').html(
|
||||
util.clean_user_content_links(sub.rendered_description)
|
||||
);
|
||||
};
|
||||
|
||||
exports.invite_user_to_stream = function (user_email, sub, success, failure) {
|
||||
@@ -458,7 +461,9 @@ exports.change_stream_description = function (e) {
|
||||
$(".stream_change_property_info"));
|
||||
},
|
||||
error: function (xhr) {
|
||||
sub_settings.find('.stream-description-editable').html(sub.rendered_description);
|
||||
sub_settings.find('.stream-description-editable').html(
|
||||
util.clean_user_content_links(sub.rendered_description)
|
||||
);
|
||||
ui_report.error(i18n.t("Error"), xhr, $(".stream_change_property_info"));
|
||||
},
|
||||
});
|
||||
|
@@ -1,3 +1,4 @@
|
||||
const util = require('./util');
|
||||
const render_subscription = require('../templates/subscription.hbs');
|
||||
const render_subscription_settings = require('../templates/subscription_settings.hbs');
|
||||
const render_subscription_table_body = require('../templates/subscription_table_body.hbs');
|
||||
@@ -162,7 +163,7 @@ exports.update_stream_description = function (sub, description, rendered_descrip
|
||||
|
||||
// Update stream row
|
||||
const sub_row = exports.row_for_stream_id(sub.stream_id);
|
||||
sub_row.find(".description").html(sub.rendered_description);
|
||||
sub_row.find(".description").html(util.clean_user_content_links(sub.rendered_description));
|
||||
|
||||
// Update stream settings
|
||||
stream_edit.update_stream_description(sub);
|
||||
|
@@ -1,3 +1,5 @@
|
||||
const util = require("./util");
|
||||
|
||||
// Below, we register Zulip-specific extensions to the handlebars API.
|
||||
//
|
||||
// IMPORTANT: When adding a new handlebars helper, update the
|
||||
@@ -73,4 +75,9 @@ Handlebars.registerHelper('tr', function (context, options) {
|
||||
return new Handlebars.SafeString(result);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper(
|
||||
"rendered_markdown",
|
||||
content => new Handlebars.SafeString(util.clean_user_content_links(content))
|
||||
);
|
||||
|
||||
window.templates = exports;
|
||||
|
@@ -347,4 +347,44 @@ exports.convert_message_topic = function (message) {
|
||||
}
|
||||
};
|
||||
|
||||
exports.clean_user_content_links = function (html) {
|
||||
const content = new DOMParser().parseFromString(html, "text/html").body;
|
||||
for (const elt of content.getElementsByTagName("a")) {
|
||||
// Ensure that all external links have target="_blank"
|
||||
// rel="opener noreferrer". This ensures that external links
|
||||
// never replace the Zulip webapp while also protecting
|
||||
// against reverse tabnapping attacks, without relying on the
|
||||
// correctness of how Zulip's markdown processor generates links.
|
||||
//
|
||||
// Fragment links, which we intend to only open within the
|
||||
// Zulip webapp using our hashchange system, do not require
|
||||
// these attributes.
|
||||
let url;
|
||||
try {
|
||||
url = new URL(elt.getAttribute("href"), window.location.href);
|
||||
} catch {
|
||||
elt.removeAttribute("href");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
// eslint-disable-next-line no-script-url
|
||||
["data:", "javascript:", "vbscript:"].indexOf(url.protocol) !== -1
|
||||
) {
|
||||
// Remove unsafe links completely.
|
||||
elt.removeAttribute("href");
|
||||
} else if (
|
||||
// We detect URLs that are just fragments by comparing the URL
|
||||
// against a new URL generated using only the hash.
|
||||
url.hash === "" || url.href !== new URL(url.hash, window.location.href).href
|
||||
) {
|
||||
elt.setAttribute("target", "_blank");
|
||||
elt.setAttribute("rel", "noopener noreferrer");
|
||||
} else {
|
||||
elt.removeAttribute("target");
|
||||
}
|
||||
}
|
||||
return content.innerHTML;
|
||||
};
|
||||
|
||||
window.util = exports;
|
||||
|
@@ -249,7 +249,9 @@ html {
|
||||
margin: 0 auto 10px;
|
||||
}
|
||||
|
||||
.register-account .terms-of-service .input-group {
|
||||
.register-account .terms-of-service .input-group,
|
||||
.register-account .default-stream-groups .input-group,
|
||||
.register-account .default-stream-groups p {
|
||||
width: 330px;
|
||||
margin: 0 auto 10px;
|
||||
}
|
||||
|
@@ -1303,12 +1303,16 @@ input.new-organization-button {
|
||||
}
|
||||
|
||||
.error_page {
|
||||
padding: 20px 0px;
|
||||
min-height: calc(100vh - 290px);
|
||||
height: 100%;
|
||||
background-color: hsl(163, 42%, 85%);
|
||||
font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.error_page .container {
|
||||
padding: 20px 0px;
|
||||
}
|
||||
|
||||
.error_page .row-fluid {
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
@@ -227,13 +227,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
#feedback_section {
|
||||
text-align: left;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid hsl(0, 0%, 88%);
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#keyboard-icon {
|
||||
position: fixed;
|
||||
bottom: 8px; /* bottom padding of .compose-content */
|
||||
|
@@ -36,7 +36,7 @@
|
||||
<i class="fa fa-trash-o fa-lg delete-draft" aria-hidden="true" data-toggle="tooltip" title="{{t 'Delete draft' }} (Backspace)"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message_content rendered_markdown restore-draft" data-toggle="tooltip" title="{{t 'Restore draft' }}">{{{content}}}</div>
|
||||
<div class="message_content rendered_markdown restore-draft" data-toggle="tooltip" title="{{t 'Restore draft' }}">{{rendered_markdown content}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -9,9 +9,7 @@
|
||||
<i class="zulip-icon bot" aria-label="{{t 'Bot' }}"></i>
|
||||
{{/if}}
|
||||
|
||||
<span class="status-message auto-select">
|
||||
{{{ status_message }}}
|
||||
</span>
|
||||
<span class="rendered_markdown status-message auto-select">{{rendered_markdown status_message}}</span>
|
||||
|
||||
{{#if edited_status_msg}}
|
||||
{{> edited_notice}}
|
||||
|
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
|
||||
{{#unless status_message}}
|
||||
<div class="message_content rendered_markdown">{{#if use_match_properties}}{{{msg/match_content}}}{{else}}{{{msg/content}}}{{/if}}</div>
|
||||
<div class="message_content rendered_markdown">{{#if use_match_properties}}{{rendered_markdown msg/match_content}}{{else}}{{rendered_markdown msg/content}}{{/if}}</div>
|
||||
{{/unless}}
|
||||
|
||||
{{#if edited_in_left_col}}
|
||||
|
@@ -24,7 +24,7 @@
|
||||
<path fill="#777" d="M128 768h256v64H128v-64z m320-384H128v64h320v-64z m128 192V448L384 640l192 192V704h320V576H576z m-288-64H128v64h160v-64zM128 704h160v-64H128v64z m576 64h64v128c-1 18-7 33-19 45s-27 18-45 19H64c-35 0-64-29-64-64V192c0-35 29-64 64-64h192C256 57 313 0 384 0s128 57 128 128h192c35 0 64 29 64 64v320h-64V320H64v576h640V768zM128 256h512c0-35-29-64-64-64h-64c-35 0-64-29-64-64s-29-64-64-64-64 29-64 64-29 64-64 64h-64c-35 0-64 29-64 64z" />
|
||||
</svg>
|
||||
</button>
|
||||
<textarea class="message_edit_content rendered_markdown" maxlength="10000" id="message_edit_content_{{message_id}}">{{content}}</textarea>
|
||||
<textarea class="message_edit_content" maxlength="10000" id="message_edit_content_{{message_id}}">{{content}}</textarea>
|
||||
<div class="scrolling_list preview_message_area" id="preview_message_area_{{message_id}}" style="display:none;">
|
||||
<div id="markdown_preview_spinner_{{message_id}}"></div>
|
||||
<div id="preview_content_{{message_id}}" class="preview_content rendered_markdown"></div>
|
||||
|
@@ -10,7 +10,7 @@
|
||||
<div class="message_content message_edit_history_content"><p>Topic: <span class="highlight_text_inserted">{{ new_topic }}</span> <span class="highlight_text_deleted">{{ prev_topic }}</span></p></div>
|
||||
{{/if}}
|
||||
{{#if body_to_render}}
|
||||
<div class="message_content rendered_markdown message_edit_history_content">{{{ body_to_render }}}</div>
|
||||
<div class="message_content rendered_markdown message_edit_history_content">{{rendered_markdown body_to_render}}</div>
|
||||
{{/if}}
|
||||
<div class="message_author"><div class="author_details">{{ posted_or_edited }} {{ edited_by }}</div></div>
|
||||
</div>
|
||||
|
@@ -16,7 +16,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom-bar">
|
||||
<div class="description rendered_markdown" data-no-description="{{t 'No description.'}}">{{{rendered_description}}}</div>
|
||||
<div class="description rendered_markdown" data-no-description="{{t 'No description.'}}">{{rendered_markdown rendered_description}}</div>
|
||||
{{#if is_old_stream}}
|
||||
<div class="stream-message-count" data-toggle="tooltip" title="{{t 'Estimated messages per week' }}">
|
||||
<i class="fa fa-bar-chart"></i>
|
||||
|
@@ -33,7 +33,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="stream-description">
|
||||
<span class="stream-description-editable editable-section description rendered_markdown" data-no-description="{{t 'No description.' }}">{{{rendered_description}}}</span>
|
||||
<span class="stream-description-editable editable-section description rendered_markdown" data-no-description="{{t 'No description.' }}">{{rendered_markdown rendered_description}}</span>
|
||||
{{#if can_change_name_description}}
|
||||
<span class="editable" data-make-editable=".stream-description-editable"></span>
|
||||
<span class="checkmark" data-finish-editing=".stream-description-editable">✓</span>
|
||||
|
@@ -1 +1 @@
|
||||
<li data-email="{{this.email}}" class="typing_notification">{{this.full_name}} is typing...</li>
|
||||
<li data-email="{{this.email}}" class="typing_notification">{{#tr this}}__full_name__ is typing...{{/tr}}</li>
|
||||
|
@@ -11,7 +11,7 @@
|
||||
<p>
|
||||
{{#tr this}}User groups allow you to <a href="/help/mention-a-user-or-group" target="_blank">mention</a> multiple users at once. When you mention a user group, everyone in the group is notified as if they were individually mentioned.{{/tr}}
|
||||
</p>
|
||||
{{#unless (and (not (eq realm_user_group_edit_policy USER_GROUP_EDIT_POLICY_MEMBERS) (not is_admin)))}}
|
||||
{{#if (or is_admin (eq realm_user_group_edit_policy USER_GROUP_EDIT_POLICY_MEMBERS))}}
|
||||
<form class="form-horizontal admin-user-group-form">
|
||||
<div class="add-new-user-group-box grey-box">
|
||||
<div class="new-user-group-form">
|
||||
@@ -31,7 +31,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
||||
<div id="user-groups" class="new-style"></div>
|
||||
|
@@ -55,7 +55,7 @@
|
||||
<a href={{this.link}} target="_blank" class="value">{{this.value}}</a>
|
||||
{{else}}
|
||||
{{#if this.rendered_value}}
|
||||
<div class="value rendered_markdown">{{{this.rendered_value}}}</div>
|
||||
<div class="value rendered_markdown">{{rendered_markdown this.rendered_value}}</div>
|
||||
{{else}}
|
||||
<div class="value">{{this.value}}</div>
|
||||
{{/if}}
|
||||
|
@@ -30,8 +30,7 @@
|
||||
</ol>
|
||||
|
||||
<h2>You're done!</h2>
|
||||
<p>If you have any questions, please contact us using the "Send feedback" button in Zulip or e-mail us at
|
||||
<a href="mailto:{{ support_email }}">{{ support_email }}</a></p>
|
||||
<p>If you have any questions, please e-mail us at <a href="mailto:{{ support_email }}">{{ support_email }}</a></p>
|
||||
|
||||
<h2>If you want to automatically transfer your existing Zephyr subscriptions</h2>
|
||||
|
||||
|
@@ -50,6 +50,7 @@
|
||||
{% include "zerver/app/settings_overlay.html" %}
|
||||
</div>
|
||||
|
||||
{% include "zerver/app/navbar_alerts.html" %}
|
||||
{% include "zerver/app/navbar.html" %}
|
||||
|
||||
<div class="fixed-app">
|
||||
|
@@ -1,32 +1,3 @@
|
||||
<div id="panels">
|
||||
<div data-process="notifications" class="alert alert-info">
|
||||
<span class="close" data-dismiss="alert" aria-label="{{ _('Close') }}">×</span>
|
||||
<div data-step="1">
|
||||
{% trans %}Zulip needs your permission to
|
||||
<a class="request-desktop-notifications alert-link">enable desktop notifications.</a>
|
||||
{% endtrans %}
|
||||
</div>
|
||||
<div data-step="2" style="display: none">
|
||||
{{ _('We strongly recommend enabling desktop notifications. They help Zulip keep your team connected.') }}
|
||||
<span class="buttons">
|
||||
<a class="alert-link request-desktop-notifications">{{ _('Enable notifications') }}</a>
|
||||
•
|
||||
<a class="alert-link exit">{{ _('Ask me later') }}</a>
|
||||
•
|
||||
<a class="alert-link reject-notifications">{{ _('Never ask on this computer') }}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div data-process="email-server" class="alert alert-info red">
|
||||
<span class="close" data-dismiss="alert" aria-label="{{ _('Close') }}">×</span>
|
||||
<div data-step="1">
|
||||
{% trans %}Zulip needs to send email to confirm users' addresses and send notifications.{% endtrans %}
|
||||
<a class="alert-link" href="https://zulip.readthedocs.io/en/latest/production/email.html" target="_blank">
|
||||
{% trans %}See how to configure email.{% endtrans %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header">
|
||||
<nav class="header-main rightside-userlist" id="top_navbar">
|
||||
<div class="column-left">
|
||||
@@ -167,13 +138,6 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="divider" role="presentation"></li>
|
||||
{% if enable_feedback %}
|
||||
<li role="presentation">
|
||||
<a href="#feedback" class="feedback" role="menuitem">
|
||||
<i class="fa fa-comment" aria-hidden="true"></i> {{ _('Feedback') }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if show_invites %}
|
||||
<li role="presentation">
|
||||
<a href="#invite" role="menuitem">
|
||||
|
40
templates/zerver/app/navbar_alerts.html
Normal file
40
templates/zerver/app/navbar_alerts.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<div id="panels">
|
||||
<div data-process="notifications" class="alert alert-info">
|
||||
<span class="close" data-dismiss="alert" aria-label="{{ _('Close') }}">×</span>
|
||||
<div data-step="1">
|
||||
{% trans %}Zulip needs your permission to
|
||||
<a class="request-desktop-notifications alert-link">enable desktop notifications.</a>
|
||||
{% endtrans %}
|
||||
</div>
|
||||
<div data-step="2" style="display: none">
|
||||
{{ _('We strongly recommend enabling desktop notifications. They help Zulip keep your team connected.') }}
|
||||
<span class="buttons">
|
||||
<a class="alert-link request-desktop-notifications">{{ _('Enable notifications') }}</a>
|
||||
•
|
||||
<a class="alert-link exit">{{ _('Ask me later') }}</a>
|
||||
•
|
||||
<a class="alert-link reject-notifications">{{ _('Never ask on this computer') }}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div data-process="email-server" class="alert alert-info red">
|
||||
<span class="close" data-dismiss="alert" aria-label="{{ _('Close') }}">×</span>
|
||||
<div data-step="1">
|
||||
{% trans %}Zulip needs to send email to confirm users' addresses and send notifications.{% endtrans %}
|
||||
<a class="alert-link" href="https://zulip.readthedocs.io/en/latest/production/email.html" target="_blank">
|
||||
{% trans %}See how to configure email.{% endtrans %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div data-process="insecure-desktop-app" class="alert alert-info red">
|
||||
<span class="close" data-dismiss="alert" aria-label="{{ _('Close') }}">×</span>
|
||||
<div data-step="1">
|
||||
{% trans %}
|
||||
You are using an old version of the Zulip desktop app with known security bugs.
|
||||
<a class="alert-link" href="https://zulipchat.com/apps" target="_blank">
|
||||
Download the latest version.
|
||||
</a>
|
||||
{% endtrans %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1,12 +1,5 @@
|
||||
<div class="right-sidebar" id="right-sidebar" role="navigation">
|
||||
<div class="right-sidebar-items">
|
||||
{% if enable_feedback %}
|
||||
<div id="feedback_section" class="new-style">
|
||||
<button type="button" class="button small rounded" id="feedback_button">
|
||||
<i class="fa fa-comment" aria-hidden="true"></i> {{ _('Send feedback') }}
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="user-list">
|
||||
<div id="userlist-header">
|
||||
<h4 class='sidebar-title' id='userlist-title' data-toggle="tooltip" title="{{ _('Filter users') }}">{{ _('USERS') }}</h4>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{% extends "zerver/emails/compiled/email_base_default.html" %}
|
||||
|
||||
{% block illustration %}
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt="{{ _('Zulip logo') }}"/>
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt=""/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{% extends "zerver/emails/compiled/email_base_default.html" %}
|
||||
|
||||
{% block illustration %}
|
||||
<img src="{{ email_images_base_uri }}/registration_confirmation.png" alt="{{ _('Turtle with envelope') }}"/>
|
||||
<img src="{{ email_images_base_uri }}/registration_confirmation.png" alt=""/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{% extends "zerver/emails/compiled/email_base_default.html" %}
|
||||
|
||||
{% block illustration %}
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt="{{ _('Zulip logo') }}"/>
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt=""/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -299,6 +299,10 @@ a.button:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.content_disabled_help_link {
|
||||
color: #15c;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{% extends "zerver/emails/compiled/email_base_default.html" %}
|
||||
|
||||
{% block illustration %}
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt="{{ _('Zulip logo') }}"/>
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt=""/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{% extends "zerver/emails/compiled/email_base_default.html" %}
|
||||
|
||||
{% block illustration %}
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt="{{ _('Zulip logo') }}"/>
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt=""/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{% extends "zerver/emails/compiled/email_base_default.html" %}
|
||||
|
||||
{% block illustration %}
|
||||
<img src="{{ email_images_base_uri }}/day2_1.png" alt="{{ _('Octopus box with heart') }}"/>
|
||||
<img src="{{ email_images_base_uri }}/day2_1.png" alt=""/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{% extends "zerver/emails/compiled/email_base_default.html" %}
|
||||
|
||||
{% block illustration %}
|
||||
<img src="{{ email_images_base_uri }}/registration_confirmation.png" alt="{{ _('Turtle with envelope') }}"/>
|
||||
<img src="{{ email_images_base_uri }}/registration_confirmation.png" alt=""/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{% extends "zerver/emails/compiled/email_base_default.html" %}
|
||||
|
||||
{% block illustration %}
|
||||
<img src="{{ email_images_base_uri }}/invitation_reminder.png" alt="{{ _('Mailbox with envelope') }}"/>
|
||||
<img src="{{ email_images_base_uri }}/invitation_reminder.png" alt=""/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -11,6 +11,14 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="missed_message">
|
||||
{% if message_content_disabled_by_realm %}
|
||||
This email does not include message content because your organization has disabled <a class="content_disabled_help_link" href="{{ realm_uri }}/help/hide-message-content-in-emails">message content appearing in email notifications</a>.
|
||||
{% elif message_content_disabled_by_user %}
|
||||
This email does not include message content because you have disabled <a class="content_disabled_help_link" href="{{ realm_uri }}/help/pm-mention-alert-notifications">message content appearing in email notifications</a>.
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -24,10 +32,12 @@
|
||||
{% endif %}
|
||||
{% if reply_to_zulip %}
|
||||
Reply to this email directly, <a href="{{ narrow_url }}">view it in Zulip</a>, or <a href="{{ realm_uri }}/#settings/notifications">manage email preferences</a>.
|
||||
{% elif not show_message_content %}
|
||||
<a href="{{ narrow_url }}">View or reply in Zulip</a>, or <a href="{{ realm_uri }}/#settings/notifications">manage email preferences</a>.<br>
|
||||
{% else %}
|
||||
<a href="{{ narrow_url }}">Reply in Zulip</a>, or <a href="{{ realm_uri }}/#settings/notifications">manage email preferences</a>.<br>
|
||||
<br>
|
||||
Do not reply to this message. This Zulip server is not
|
||||
Do not reply to this email. This Zulip server is not
|
||||
configured to accept incoming emails (<a href="https://zulip.readthedocs.io/en/latest/production/email-gateway.html">help</a>).
|
||||
|
||||
{% endif %}
|
||||
|
@@ -6,6 +6,14 @@
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% if message_content_disabled_by_realm %}
|
||||
This email does not include message content because your organization has disabled message content appearing in email notifications.
|
||||
See {{ realm_uri }}/help/hide-message-content-in-emails for more details.
|
||||
{% elif message_content_disabled_by_user %}
|
||||
This email does not include message content because you have disabled message content appearing in email notifications.
|
||||
See {{ realm_uri }}/help/pm-mention-alert-notifications for more details.
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
--
|
||||
@@ -18,11 +26,14 @@ You are receiving this because you have email notifications enabled for this str
|
||||
{% if reply_to_zulip %}
|
||||
Reply to this email directly, or view it in Zulip:
|
||||
{{ narrow_url }}
|
||||
{% elif not show_message_content %}
|
||||
View or reply in Zulip:
|
||||
{{ narrow_url }}
|
||||
{% else %}
|
||||
Reply in Zulip:
|
||||
{{ narrow_url }}
|
||||
|
||||
Do not reply to this message. This Zulip server is not configured to accept
|
||||
Do not reply to this email. This Zulip server is not configured to accept
|
||||
incoming emails. Help:
|
||||
https://zulip.readthedocs.io/en/latest/production/email-gateway.html
|
||||
{% endif %}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{% extends "zerver/emails/compiled/email_base_default.html" %}
|
||||
|
||||
{% block illustration %}
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt="{{ _('Zulip logo') }}"/>
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt=""/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -5,7 +5,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block illustration %}
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt="{{ _('Zulip logo') }}"/>
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt=""/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{% extends "zerver/emails/compiled/email_base_default.html" %}
|
||||
|
||||
{% block illustration %}
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt="{{ _('Zulip logo') }}"/>
|
||||
<img src="{{ email_images_base_uri }}/email_logo.png" alt=""/>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user