Compare commits

...

592 Commits

Author SHA1 Message Date
Tim Abbott
5ffe1439eb Add changelog for Zulip 1.3.11 release. 2016-05-02 20:39:51 -07:00
Tim Abbott
d9e4968d6f Increase maximum URL length for RealmEmoji to 1000.
The default of 200 was shorter than the Camo URLs use by Zulip.
2016-05-02 19:02:56 -07:00
Tim Abbott
5bd94c15c7 Use camo to avoid mixed content warnings when displaying emoji. 2016-05-02 17:21:31 -07:00
Tim Abbott
52c1e8ac7d Run a local camo server in voyager production environments.
Camo is a caching image proxy, used in Zulip to avoid mixed-content
warnings by proxying HTTP image content over HTTPS.  We've been using
it in zulip.com production for years; this change makes it available
in standalone Zulip deployments.
2016-05-02 17:21:31 -07:00
Tim Abbott
65207477c4 bugdown: Annotate emoji handleMatch function. 2016-05-02 17:01:09 -07:00
Tim Abbott
4241e01854 realm emoji: Fix realm-time updating of admin emoji table.
Previously the realm emoji table on the admin page didn't update
properly in the event that another user added or removed emoji.
2016-05-02 17:00:47 -07:00
Tim Abbott
48a578d003 travis: hold expensive to upgrade packages in Travis CI.
This should save a few minutes of time running the production test
suite.  This is part of solving #722.
2016-05-02 16:59:21 -07:00
Tim Abbott
79327a61ae travis: Do an apt-get update before the apt upgrade.
This should save several minutes off the Travis CI `production`
suite's runtime, since previously we were doing the full apt upgrade
process twice, resulting in things like multiple expensive rebuilds of
the initramfs.
2016-05-02 16:35:46 -07:00
Eklavya Sharma
27f12b2de3 Annotate tools/lister.py. 2016-05-01 23:04:09 +05:30
Eklavya Sharma
247cdf578b Add dependencies to setup-py3k.
Add 'six' to setup-py3k, because it is being used in tools/lister.py.
Add 'typing' to setup-py3k, so that tools/lister.py can be type
annotated in the future.
2016-05-01 10:47:04 +05:30
Eklavya Sharma
2d3f9c8fb9 tools/lister.py: Use default arguments in add_argument.
Use the `default` parameter of ArgumentParser.add_argument
instead of manually setting default using the `x = x or []` pattern.
2016-05-01 07:01:52 +05:30
Tim Abbott
aa3549097d Mark Docker development instructions as experimental. 2016-04-29 16:15:14 -07:00
Tim Abbott
f06c8c7cc2 Update *.readthedocs.org => *.readthedocs.io.
ReadTheDocs has moved their hosting of user project websites to the
new readthedocs.io domain.
2016-04-29 16:00:08 -07:00
Tim Abbott
4644967afc dropbox preview: Remove preview_fail.png error condition.
Since we don't have a stable way to get the Dropbox preview failure
image (and it was sorta a weird setup anyway), it seems best to just
remove the condition.
2016-04-29 15:40:52 -07:00
Tim Abbott
4be3c4afd6 Use mocks so we can re-enable Dropbox integration tests. 2016-04-29 15:27:43 -07:00
Tim Abbott
8df58432f6 Clear user filter after pressing enter to start compose.
Previously, the user list would remain filtered after a user hit enter
to start composing a message to a user, leaving them in a state with a
partial user list.

Fixes #360.
2016-04-29 15:01:41 -07:00
Ashish Kumar
31408d639e Type annotation of zerver/lib/cache.py. 2016-04-29 14:43:48 -07:00
Tim Abbott
4c3118b39f Document new zulip-ios mailing list. 2016-04-29 14:21:01 -07:00
Ashish Kumar
48be2e33f8 Delete old route for /json/get_public_streams. 2016-04-29 12:57:57 -07:00
Ashish Kumar
b5ab4d45f9 Replace /json/get_public_streams with REST style route. 2016-04-29 12:57:57 -07:00
Tim Abbott
362a622f1f Add documentation on translating Zulip. 2016-04-28 21:57:10 -07:00
Tim Abbott
27b8e8b294 Add the Zulip 2016 roadmap document. 2016-04-28 21:55:33 -07:00
Tim Abbott
a626f4558c README.md: Clean up guide to new contributions. 2016-04-28 21:07:42 -07:00
Eklavya Sharma
d3f2d17ee9 Use a shallow copy of Zulip's Django fork.
This will substantially improve the performance of running provision
on systems with a relatively slow network.

Fixes #704.
2016-04-28 16:07:53 -07:00
Tim Abbott
af4203b41b Stop using initial password for newly activated users.
Previously we needed to use a specified password when activating a
formerly mirror dummy user, in order for that user to be able to
(re)set their password and login.  Now that we have our own password
reset form, this is no longer required.
2016-04-28 14:28:09 -07:00
Tim Abbott
89d9060aab Add logging for failures in password reset form.
This may be useful for monitoring abuse issues.
2016-04-28 14:28:09 -07:00
Tim Abbott
a0430c02ce Allow users who haven't set a password to set one.
Previously, if a user had only authenticated via Google auth, they
would be unable to reset their password in order to set one (which is
needed to setup the mobile apps, for example).
2016-04-28 14:27:43 -07:00
Antek Grzanka
646ea3214a Add Taiga integration. 2016-04-28 13:44:53 -07:00
Tim Abbott
755695d3c0 bugdown: Add type: ignore for fenced_code import.
This is a workaround to allow us to type-check files that depend on
this.  Ideally in the future we'll fix the type errors in
fenced_code.py.
2016-04-28 12:50:47 -07:00
Tim Abbott
7a81524c97 event_queue: Fix import from wrong file. 2016-04-28 12:46:21 -07:00
Tim Abbott
d61c8f91cf run-mypy: Link to docs on ReadTheDocs. 2016-04-28 12:35:54 -07:00
Eklavya Sharma
b60141fd84 Add documentation on the using the mypy static type checker.
[with substantial tweaks by tabbott]
2016-04-28 12:29:45 -07:00
Eklavya Sharma
4310e6d224 Add tools/install-mypy.
This provides a way for users not using provision.py to install mypy.

[with substantial tweaks by tabbott]
2016-04-28 12:29:12 -07:00
Eklavya Sharma
1041115b38 Add code to install mypy from provision.py. 2016-04-28 12:28:24 -07:00
Eklavya Sharma
3601b9eda9 tools/run-mypy: Use mypy from zulip-py3-venv if present. 2016-04-28 12:28:24 -07:00
Eklavya Sharma
c80f699321 Add tools/run-mypy to Travis checks.
Fixes #635.
2016-04-28 10:03:35 -07:00
Eklavya Sharma
1af4334887 Add tools/run-mypy.
Since a lot of files don't pass the mypy check, a long list of
files to be excluded from mypy check has been specified.
2016-04-28 10:03:35 -07:00
Eklavya Sharma
c220c61dbd tools/check-py3: Speed up and add --find-fixers.
Now tools/check-py3 will by default run all fixers together.  This is
quicker but doesn't indicate which fixers caused the failure.  The
newly added option --find-fixers falls back to the old way of checking
each fixer separately if the quick check fails.

Fixes #710.
2016-04-28 10:01:17 -07:00
Eklavya Sharma
22d407fe0b docs/testing.rst: Fix typo and name of a package.
* Remove duplicate words.
* Replace `futurize` by `future`.
2016-04-28 15:19:22 +05:30
Tim Abbott
3e5ad69ffc Extract camo encoding to a library. 2016-04-27 22:23:40 -07:00
Tim Abbott
302da832fa lint: Enforce whitespace between : and value in dicts. 2016-04-27 22:23:40 -07:00
Tim Abbott
aebe7334a4 style: Fix missing :s between dict keys and values. 2016-04-27 22:23:40 -07:00
Tim Abbott
02ab03ec7a MITNameTest: Mock network access from Hesiod lookups.
The purpose of these tests is to check the logic, not that DNS is
working on the relevant machine.
2016-04-27 22:23:40 -07:00
Tim Abbott
e9a76f98c3 Vagrantfile: Fix using Vagrant with LXC >= 2.
Apparently LXC 2 removed support for the `-B best` option in
lxc-create, and Vagrant hasn't been updated appropriately yet, so we
need to add a workaround to explicitly specify a backing store.

Fixes #718.

This manifested as errors of the form:

"""
There was an error executing ["sudo",
"/usr/local/bin/vagrant-lxc-wrapper", "lxc-create", "-B", "best",
"--template",
"/home/tabbott/.vagrant.d/gems/gems/vagrant-lxc-1.2.1/scripts/lxc-template",
"--name", "zulip_default_1461801696512_85064", "--", "--tarball",
"/home/tabbott/.vagrant.d/boxes/fgrehm-VAGRANTSLASH-trusty64-lxc/1.2.0/lxc/rootfs.tar.gz",
"--config",
"/home/tabbott/.vagrant.d/boxes/fgrehm-VAGRANTSLASH-trusty64-lxc/1.2.0/lxc/lxc-config"]
"""
2016-04-27 17:07:03 -07:00
Tim Abbott
c9359bd75a test_hooks: Fix missing assert_json_success checks.
Several recently merged webhooks were incorrectly not checking that
the actual webhook result didn't return an error.  While they would
usually still fail in most cases when checking whether the message
came back correctly, this hid the root cause errors and thus made it
much harder to debug.
2016-04-27 13:25:21 -07:00
Tim Abbott
e6cfd917a5 Fix settings.RATE_LIMITING=False for webhooks.
We were incorrectly applying the rate limiting rules to webhooks even
if rate limiting was disabled (as in the test suite), causing test
failures when the total number of webhook tests in Zulip got too high.
2016-04-27 13:17:28 -07:00
Eklavya Sharma
b4555e58c8 tools/check-py3: Update references in output.
Replace occurrences of 'py3k' by 'check-py3' in echo output.
2016-04-27 10:34:52 -07:00
Tim Abbott
c83999fe52 Fix EXTRA_INSTALLED_APPS in development.
In theory, tools like populate_db should probably be in zerver, not
zilencer, but until we migrate them out, we need to include these in
EXTRA_INSTALLED_APPS in development.
2016-04-26 21:55:31 -07:00
Tim Abbott
8905216df5 Automate inclusion of urls.py files for EXTRA_INSTALLED_APPS.
By removing this hardcoding of the list of valid extra apps, we make
it a lot easier to add additional pluggable apps to the Zulip
codebase.
2016-04-26 21:39:39 -07:00
Tim Abbott
bf50dd7771 Simplify zilencer urls.py configuration to a single file.
The previous separated-out configuration wasn't helping us, and this
makes it easier to make the extra installed applications pluggable in
the following commits.
2016-04-26 21:35:07 -07:00
Tim Abbott
2b30b670e0 Factor out EXTRA_INSTALLED_APPS setting. 2016-04-26 21:28:51 -07:00
Tim Abbott
6e1e4aaef6 postgres-init-db: Add POSTGRES_USER argument. 2016-04-26 15:27:35 -07:00
Tim Abbott
dc772518e7 Don't chown supervisor socket if it doesn't exist. 2016-04-26 15:27:35 -07:00
Tim Abbott
6a3c775842 install: Ensure prod-static/serve is created. 2016-04-26 15:27:35 -07:00
Tim Abbott
bb25b6060e install: Avoid unnecessarily storing apt key under /root. 2016-04-26 15:07:53 -07:00
Tim Abbott
e9416a9fb2 install: Add PUPPET_CLASSES variable. 2016-04-26 15:06:37 -07:00
Tim Abbott
a9d86a3620 install: Add DEPLOYMENT_TYPE variable. 2016-04-26 15:04:32 -07:00
Tim Abbott
68c6d514e8 install: Add ADDITIONAL_PACKAGES option. 2016-04-26 15:02:28 -07:00
Vladislav Manchev
f5e6176aea Add custom realm emoji UI to administration page. 2016-04-26 13:15:54 -07:00
Tim Abbott
f5fe2d4bf7 Fix case-insensitive typeahead for emoji. 2016-04-26 13:14:28 -07:00
Tim Abbott
abacd9b2da Integration guide: Document need to edit urls.py. 2016-04-26 11:50:19 -07:00
Tim Abbott
e4aab64464 Sort webhook integration URL definitions.
This will merge conflict with every new integraiton in flight, which
is unfortunate, but will make there be fewer merge conflicts as people
add new webhooks in the future (currently, every pair of new
integrations conflict because folks are adding them all at the end,
whereas after this change, there will only be merge conflicts when
adding two integrations near each other alphabetically).
2016-04-26 11:49:33 -07:00
Vishnu Ks
fe4a03fd01 Move narrowed_msg_list to message_list.js. 2016-04-26 10:25:11 -07:00
Tomasz Kolek
12fc4f047c test_helpers: Create get_last_message helper. 2016-04-26 09:54:02 -07:00
Tomasz Kolek
5fbda3a9c1 Add codeship integration. 2016-04-26 09:54:01 -07:00
David Payne
8c62a27769 Add teamcity webhook integration.
This integration relies on the Teamcity "tcWebHooks" plugin which is
available at
https://netwolfuk.wordpress.com/category/teamcity/tcplugins/tcwebhooks/

It posts build fail and success notifications to a stream specified in
the webhook URL.

It uses the name of the build configuration as the topic.

For personal builds, it tries to map the Teamcity username to a Zulip
username, and sends a private message to that person.
2016-04-26 09:45:26 -07:00
Tim Abbott
672a431fba Expand documentation on tools/check-py3. 2016-04-25 16:46:46 -07:00
Eklavya Sharma
ae46d425b6 Add info about tools/check-py3 in docs/testing.rst.
Fixes #701.
2016-04-26 03:32:29 +05:30
Vishnu Ks
ae49ad383d Rename all function on MessageList to all_messages. 2016-04-25 13:45:18 -07:00
Eklavya Sharma
cbba7202e6 Add future and modernize to requirements.txt
Also improve tools/travis/setup-py3k in these ways:
* remove sudo
* add --no-deps to pip install
* specify versions in pip install
2016-04-25 09:50:32 -07:00
Eklavya Sharma
1ce2d26679 Prevent check-py3 from failing on no files.
Now tools/travis/check-py3 does not fail unexpectedly when there are
no python files in the current directory.
2016-04-25 09:50:32 -07:00
Eklavya Sharma
b4009c28d0 Move py3k and add a travis wrapper for it.
Move tools/travis/py3k to tools/check-py3.
Add tools/travis/py3k which calls tools/check-py3.
2016-04-25 09:50:32 -07:00
Eklavya Sharma
101148c49e Fix typo and formatting in docs. 2016-04-25 20:29:29 +05:30
Tim Abbott
21161a8adb Add memcached_prefix to .gitignore. 2016-04-22 17:32:44 -07:00
Tim Abbott
74ed9fabd0 Integration guide: Expand instructions on screenshots. 2016-04-21 18:17:26 -07:00
Vishnu Ks
35b0af2852 Move all_msg_list to message_list.js. 2016-04-21 17:46:21 -07:00
Tomasz Kolek
c74483e69e github_webhook: change double quotes to single quotes for consistency. 2016-04-21 17:04:25 -07:00
Tomasz Kolek
09e40b27c2 github_webhook: throw an exception on unhandled events types. 2016-04-21 17:03:58 -07:00
Tomasz Kolek
fafc9cb742 github_webhook: remove redundant parenthesis. 2016-04-21 17:02:49 -07:00
Tomasz Kolek
43b0cfaebc github_webhook: Use more one-line pythonic assignments. 2016-04-21 17:00:47 -07:00
Tomasz Kolek
decb686255 github_webhook: factor out is_test_repository function. 2016-04-21 16:57:19 -07:00
Tomasz Kolek
e1079d8475 github_webhook: extract the constants to the top of the file. 2016-04-21 16:56:44 -07:00
Tim Abbott
84e23dd015 Document code copied from Django and then modified. 2016-04-21 14:59:39 -07:00
Tim Abbott
ae047f8551 Fix slightly ugly login page URL of /login?next=/. 2016-04-21 14:59:39 -07:00
Tim Abbott
8a278cbe3a Switch to using a Zulip version of @login_required.
Currently the code is the unmodified Django upstream implementation;
this commit is preparation for modifying it.
2016-04-21 14:59:39 -07:00
Tim Abbott
d9dba5d2c2 deactivate_realm: Improve error handling for unknown realm. 2016-04-21 09:02:00 -07:00
Tim Abbott
c2237c60c0 deactivate_realm: Fix help string. 2016-04-21 09:02:00 -07:00
Tim Abbott
d890011442 Add a script to reactivated deactivated realms. 2016-04-21 09:02:00 -07:00
Tim Abbott
79297898f1 Remove obsolete AppleDeviceToken model. 2016-04-20 21:51:52 -07:00
Tim Abbott
49799440a4 Replace use of django-guardian with fields on UserProfile.
As documented in https://github.com/zulip/zulip/issues/441, Guardian
has quite poor performance, and in fact almost 50% of the time spent
running the Zulip backend test suite on my laptop was inside Guardian.

As part of this migration, we also clean up the old API_SUPER_USERS
variable used to mark EMAIL_GATEWAY_BOT as an API super user; now that
permission is managed entirely via the database.

When rebasing past this commit, developers will need to do a
`manage.py migrate` in order to apply the migration changes before the
server will run again.

We can't yet remove Guardian from INSTALLED_APPS, requirements.txt,
etc. in this release, because otherwise the reverse migration won't
work.

Fixes #441.
2016-04-20 21:51:52 -07:00
Vishnu Ks
ee39f5009f Make message_list.js work like the other modules. 2016-04-20 15:29:30 -07:00
Tim Abbott
28d1a3105c models: Add a __repr__ for Client. 2016-04-20 15:26:51 -07:00
Tim Abbott
552caf661a Caching: Fix 'update_fields' not being present in .delete() 2016-04-20 15:12:53 -07:00
Tim Abbott
9c56027627 lint: Add CSS lint rule for whitespace after {. 2016-04-20 11:50:01 -07:00
Tim Abbott
a46b5d7bbe Add lint check for missing whitespace after =. 2016-04-20 11:37:03 -07:00
Tim Abbott
a72385246e Fix missing whitespace after '=' in python/js code. 2016-04-20 11:36:14 -07:00
David Payne
ece96ef3fe Jira's "issue created" message should @-notify the assignee. 2016-04-20 10:54:30 -07:00
Tomasz Kolek
82f1cdb085 Add send_webhook_fixture_message command.
This tool simplifies the process of producing nice screenshots for
documenting webhook integrations.

Fixes #658.
2016-04-20 10:45:27 -07:00
Eklavya Sharma
c7a93cba22 Add tests for checking image validity in SetAvatarTest. 2016-04-20 12:27:23 +05:30
Eklavya Sharma
af7c3de5f5 Add SetAvatarTest to test_upload.py.
Test multiple file uploads for /json/set_avatar.
Test no file upload for /json/set_avatar.
Add test images for SetAvatarTest.
2016-04-20 12:23:22 +05:30
Eklavya Sharma
126273b1e7 Add a test for local file uploads. 2016-04-20 12:00:13 +05:30
Eklavya Sharma
4e18d856e3 Prevent 500 error when user uploads invalid avatar.
When uploaded avatar image is not a valid image file, PIL raises
IOError. Catch the IOError raised by PIL and raise JsonableError.
This will return a response with status code 400.
2016-04-20 12:00:13 +05:30
Eklavya Sharma
2d60a1d0f3 Move upload-related tests to test_upload.py.
Move S3Test, FileUploadTest and SanitizeNameTests from
test_external.py to test_upload.py.
2016-04-19 16:48:30 -07:00
Eklavya Sharma
c75c5fb3e1 Use a different uploads directory when running tests. 2016-04-19 16:48:30 -07:00
Tim Abbott
1b988de30a Integration guide: clean up description of plugin integrations. 2016-04-19 16:21:33 -07:00
Tim Abbott
92f9a789b8 integration guide: Improve introduction. 2016-04-19 16:21:33 -07:00
Tim Abbott
0669262ccb /integrations: Document how to contribute integrations. 2016-04-19 16:21:33 -07:00
Vishnu Ks
3179434f93 Move keep_pointer_in_view to pointer.js. 2016-04-19 15:23:45 -07:00
Vishnu Ks
b655e090a6 check_all: Move viewport to modules list.
It was previously incorrectly listed as a global variable.
2016-04-19 15:23:24 -07:00
Tim Abbott
a2b59b8b51 lint: Check whitespace rules for txt files. 2016-04-14 14:36:29 -07:00
Tim Abbott
78febc3abb Fix missing newlines at end of .txt files. 2016-04-14 14:36:29 -07:00
Tim Abbott
39950b8f4f lint: Check whitespace rules in markdown files too. 2016-04-14 14:30:29 -07:00
Tim Abbott
1a162ecb97 Fix tab-based whitespace in stress-test README file. 2016-04-14 14:30:29 -07:00
Tim Abbott
2b76f6223e Make 'no newline at end of file' lint error more actionable.
There's a handy sed command to fix this, so we might as well document
it.
2016-04-14 10:55:06 -07:00
Tim Abbott
e71d8bb4b6 lint-all: Require newlines at end of JSON files. 2016-04-14 10:49:12 -07:00
Tim Abbott
5195d1ecb7 Fix missing newlines at ends of JSON files. 2016-04-14 10:48:52 -07:00
Tim Abbott
1bf11f6b7f Split FileUploadTest out of S3Test.
S3Test is now only the S3-specific test (which isn't even run), so we
can now invest in making FileUploadTest have good coverage of the
(local) file upload code paths.
2016-04-14 10:35:10 -07:00
Sumana Harihareswara
4ce4f88a03 Fix formatting in directory-structure.rst. 2016-04-13 15:44:10 -07:00
Tim Abbott
74abd47684 Fix EMAIL_GATEWAY_BOT not being set by default in production.
Previously the DEFAULTS value of None for EMAIL_GATEWAY_BOT was
overriding the initialization code.
2016-04-13 13:19:02 -07:00
Tim Abbott
ae48f6394b migrations: Disable prompting about content-type deletion.
The main function of prompting inside `manage.py migrate` is to ask
the user if they want to delete stale content-types, which is
unimportant and likely scary, so we disable doing so.
2016-04-13 13:19:02 -07:00
Tim Abbott
d0f2c46f25 generate_test_credentials: Use the email variable properly.
This makes it a bit easier to change the email in the test
credentials.
2016-04-13 13:19:02 -07:00
Tim Abbott
26463bb34d Fix nondeterministic subscriptions for default test users.
Previously, the UserProfile objects were created in the order
generated by a Set, which meant tests would randomly start failing if
the code that runs before this part of populate_db changed (and thus
caused the Set object used to pass users into bulk_create_users to
have a different order when enumerated).

This fixes the issue in two ways -- one by sorting the users inside
bulk_create_users, and second by attaching subscriptions to users
based on a deterministic ordering.
2016-04-13 13:19:02 -07:00
Tim Abbott
81143a8c98 02-narrow: Use Denmark as the test second stream.
It's less easily confused with "Verona", and prevents the next commit
from breaking the tests (since Iago will lose his subscription to
"Venice").
2016-04-13 13:19:02 -07:00
Tim Abbott
f6edc21981 Change stream used in test_get_old_messages_with_only_searching_anchor.
This prevents this test from breaking when in a few commits we fix a
nondeterminism issue in the populate_db test fixtures.
2016-04-13 13:19:02 -07:00
Tim Abbott
ffccb572f0 Don't autoreload Tornado when running inside test suite.
The restarted Tornado processes seemed to escape the process group and
thus continue running after run-dev.py finished.

While we're at it, we don't need to dump/reload event queues in the
test suite either.
2016-04-13 13:19:01 -07:00
Tim Abbott
98d5f64f36 webpack: Use the correct port in Casper tests.
Previously we used 9994 unconditionally, whereas we should be using
9984 or 9994 depending whether it's being run manually or via the
Casper tests.
2016-04-13 13:18:38 -07:00
Tim Abbott
47879c5e00 Fix nondeterminism in test_successful_subscriptions_add.
Previously this test would fail if the streams list generated by
populate_db contained more than 2 streams.
2016-04-13 13:18:24 -07:00
Tim Abbott
2e32a7f05d Move by-hand proxy setup section closer to the actual instructions. 2016-04-13 09:06:20 -07:00
Vishnu Ks
859a4eeaf4 Add support for using and configuring vagrant-proxyconf.
Users can now specify proxy settings for vagrant in a new
~/.zulip-vagrant-config configuration file.
2016-04-13 09:06:03 -07:00
Vishnu Ks
35f70e9dac Move 5 legacy global variables to pointer.js.
Move recenter_pointer_on_display, suppress_scroll_pointer_update,
fast_forward_pointer, furthest_read, and server_furthest_read to
a new pointer module in pointer.js.
2016-04-12 10:56:54 -07:00
Tim Abbott
fb55fcef1e Fix missing zerver/tests/__init__.py. 2016-04-11 22:34:22 -07:00
Tim Abbott
be96cf809d Move Zulip backend tests to zerver.tests. 2016-04-11 22:16:09 -07:00
Ashish
1bf644369f Delete old route for json/update_active_status. 2016-04-11 21:38:23 -07:00
Ashish
78b9f45bf7 Delete old route for json/update_pointer. 2016-04-11 21:38:23 -07:00
Ashish
9429358795 Delete old route for /json/get_profile. 2016-04-11 21:38:23 -07:00
Ashish
86fb7103fa Delete old route for json/change_enter_sends. 2016-04-11 21:38:23 -07:00
Ashish
42fe918138 Delete old route for json/get_old_messages. 2016-04-11 21:38:23 -07:00
Ashish
cfefc94200 Delete old route for json/set_alert_words. 2016-04-11 21:38:23 -07:00
Ashish
c0a218edfc Delete old route for /json/update_message_flags. 2016-04-11 21:38:23 -07:00
Ashish
a12006d86f Replace /json/update_active_status with REST style route. 2016-04-11 21:38:23 -07:00
Ashish
6356584f84 Replace /json/update_pointer with REST style route. 2016-04-11 21:38:23 -07:00
Ashish
b8ec8f5ef0 Replace /json/get_profile with REST style route. 2016-04-11 21:38:23 -07:00
Ashish
679b4e5807 Replace /json/change_enter_sends with REST style route. 2016-04-11 21:38:23 -07:00
Ashish
cb8da46bbf Replace /json/get_old_messages with REST style route. 2016-04-11 21:38:23 -07:00
Ashish
8fc8717409 Replace json/set_alert_words with REST style route. 2016-04-11 21:38:22 -07:00
Ashish
41993ef2f5 Replace /json/update_message_flags with REST style route. 2016-04-11 21:38:22 -07:00
Ashish
dac4e58b91 Changes REST API backend route for /json/change_enter_sends. 2016-04-11 21:11:51 -07:00
Tim Abbott
73f2d67ba1 Document how to configure Vagrant LXC with passwordless sudo. 2016-04-10 17:37:52 -07:00
Tim Abbott
7d64bd51f5 provision: Don't install recursive pip dependencies.
This should prevent future issues like the wrong cryptography module
being in requirements.txt.
2016-04-10 17:37:52 -07:00
Tim Abbott
00a92b5827 Add missing requests_oauthlib dependency. 2016-04-10 17:37:52 -07:00
Tim Abbott
b29cb1dfb8 provision: Remove now-unnecessary patching of PATH. 2016-04-10 17:37:52 -07:00
Tim Abbott
6de15606f9 provision: Add preliminary support for Ubuntu Xenial.
This won't actually work because we don't have a tsearch_extras
package built for postgres 9.5.
2016-04-10 17:37:52 -07:00
Tim Abbott
f6a7b192a4 requirements: Upgrade cffi and cryptography libs in development.
The older versions apparently don't build on Ubuntu Xenial.
2016-04-10 17:37:52 -07:00
Tim Abbott
3c74bf000f provision: Add installing recent npm to provisioning process.
This fixes a problem where the version of NPM installed in development
environments was too old.
2016-04-10 17:37:43 -07:00
Tim Abbott
5733c32705 Fix install-phantomjs being called before chdir to ZULIP_PATH. 2016-04-10 17:33:37 -07:00
Tim Abbott
52fc1c71bc provision: Rewrite using subprocess module instead of sh.
The `with sh.sudo` pattern that we were using in python-sh was
deprecated, and emperically hangs on Ubuntu xenial.  Since in general
the use of python-sh/python-pbs caused trouble (requiring extra
dependencies, confusing syntax), this just removes it.

We replace it with a new zulip_tools.py library function that echoes
the command line and streams the output.

We do the same to install-phantomjs so we can remove that dependency.
2016-04-10 17:33:19 -07:00
Vishnu Ks
2ac5271091 Move global variable have_scrolled_away_from_top to ui.js. 2016-04-10 10:55:16 -07:00
Tim Abbott
fcced9561d provision: Add dependency on ipython.
ipython dramatically improves the experience of `manage.py shell`.
2016-04-09 10:23:13 -07:00
Kumar
4eced69228 Make subscriptions page error bar visible even when scrolled down.
Previously, the Zulip subscriptions page's error bar would always be
at the very top of the scrollable view, and thus would likely be out
of view when an error happened.  This fixes it by having the error bar
always placed below the search box (and thus visible regardless of
where in the scrollable streams view we are).

Fixes: #515.

[commit message and comments expanded by tabbott]
2016-04-08 21:04:43 -07:00
Tim Abbott
9584ae1ab8 Add CSS linter for missing space after : in rules. 2016-04-08 21:04:43 -07:00
Tim Abbott
f4bd35678e Fix missing whitespace after :s in CSS. 2016-04-08 21:04:43 -07:00
Tim Abbott
64e527ff34 Fix ordering of documentation blocks in /integrations.html. 2016-04-08 20:29:45 -07:00
Anindya Chakravarti
efb7c902de Correct alphabetical order of integrations page. 2016-04-08 20:26:47 -07:00
Tim Abbott
b61d73fc93 Delete unused old StreamColor model. 2016-04-08 13:06:04 -07:00
Tim Abbott
877b4af24a Replace platform.codename and friends with lsb_release.
The old code was producing incorrect output of "debian" as the release
on Travis CI's Ubuntu Trusty nodes.
2016-04-08 12:35:08 -07:00
Tim Abbott
6969c26dfa provision: Fix hardcoding of trusty codename. 2016-04-08 12:17:45 -07:00
Tim Abbott
64973fc4e6 provision: Refactor postgres version to be a variable. 2016-04-08 12:17:45 -07:00
Tim Abbott
7fe9a6b74b provision: check arch and codename earlier in setup process. 2016-04-08 12:17:45 -07:00
Tim Abbott
7dd9e93f9b provision: Exit with an error with unsupported platform. 2016-04-08 12:17:45 -07:00
Tim Abbott
5d13d62057 provision: simplify logging configuration. 2016-04-08 12:17:45 -07:00
Tim Abbott
fc4e8730f3 provision: Compute ZULIP_PATH dynamically. 2016-04-08 12:17:45 -07:00
Aristeidis Fkiaras
0058ccbdb0 Move global unread_messages_read_in_narrow to unread.js. 2016-04-08 12:11:47 -07:00
Tim Abbott
209e6ef7a1 Run trailing whitespace linter on bash files. 2016-04-08 11:52:11 -07:00
Tim Abbott
caba24b2af Fix existing trailing whitespace in bash scripts. 2016-04-08 11:52:11 -07:00
Tim Abbott
4fa63c29ca Run whitespace linters on html files. 2016-04-08 11:52:11 -07:00
Tim Abbott
ba30713078 Fix whitespace linting errors in html files. 2016-04-08 11:52:11 -07:00
Tim Abbott
d670e902a9 Run whitespace linters on handlebars templates. 2016-04-08 11:52:11 -07:00
Tim Abbott
88b0c12193 Fix whitespace linting errors in handlebars templates. 2016-04-08 11:52:11 -07:00
Tim Abbott
c6d01ab76b Run whitespace linters on CSS files. 2016-04-08 11:47:10 -07:00
Tim Abbott
1b84617771 Don't skip running python custom linters inside comments.
This fixes an issue where we weren't checking for trailing whitespace
in comments.
2016-04-08 11:47:10 -07:00
Tim Abbott
14b5e265c2 Remove unuseful suspicious code lint check. 2016-04-08 11:47:10 -07:00
Ashish
9cfa7d5765 Annotation of zerver/lib/handlers.py. 2016-04-08 11:18:36 -07:00
Ashish
86a8d3d0f5 Annotation of zerver/lib/rate_limiter.py 2016-04-08 11:18:36 -07:00
Ashish
2f8c717e52 List to tuple conversion for consistency in user rules zerver/lib/rate_limiter.py. 2016-04-08 11:18:35 -07:00
Ashish
8abca4f319 Annotation of zerver/lib/migrate.py 2016-04-08 11:18:35 -07:00
Ashish
038af80889 Annotation of zerver/lib/statistics.py 2016-04-08 11:11:58 -07:00
Ashish
2cf8731444 Annotation of zerver/lib/alert_words.py 2016-04-08 11:11:58 -07:00
Anindya Chakravarti
f3d03d89b4 Add integration for Yo App.
[includes some small tweaks by tabbott]
2016-04-08 11:02:10 -07:00
Tomasz Kolek
44ed9da7f0 Add pingdom integration. 2016-04-08 10:36:29 -07:00
Vishnu Ks
fe77559164 Fix broken link to Google Play store badge. 2016-04-08 08:37:17 -07:00
Tim Abbott
efd14e7ad9 Revert "Exclude 'from typing import *' from linter."
This reverts commit d936bf61f9.

We no longer need this since we've migrated to specifying the
dependencies in the typing module that we're actually using.
2016-04-07 14:12:18 -07:00
Tim Abbott
a1b306f9ce Finish purging 'fromt typing import *' from Zulip codebase. 2016-04-07 14:11:21 -07:00
Varshit
4e1060076d Purge 'from typing import *' from zerver/.
This is a partial implementation of #636.
2016-04-07 14:07:07 -07:00
Eklavya Sharma
5f03c1444e Remove duplicate module zerver/views/webhooks.py.
Also move type annotations from zerver/views/webhooks.py to
appropriate files in zerver/views/webhooks.py.
2016-04-07 12:37:22 +05:30
Tim Abbott
a7f83c9e05 Fix check_postgres_replication_lag nagios command. 2016-04-06 15:30:51 -07:00
Tim Abbott
991341867c nagios: Remove unnecessary dependency on netcat. 2016-04-06 15:30:40 -07:00
Tim Abbott
c92221dcd3 Remove old humbughq apache configuration. 2016-04-06 15:30:36 -07:00
Tim Abbott
4855296771 puppet: Migrate check_postgres plugins to postgres_common.pp. 2016-04-06 15:20:36 -07:00
Tim Abbott
e413d4e153 Move webpack configuration to tools/. 2016-04-06 08:09:55 -07:00
Tim Abbott
69a8925076 check_user_zephyr_mirror_liveness: Fix importing settings. 2016-04-05 13:27:04 -07:00
Tim Abbott
b229767605 Update Zulip Nagios plugin documentation.
This completes the effort to move the Zulip Nagios plugins to be
available in the modules that they actually are a part of.

Fixes: #371.
2016-04-05 13:27:04 -07:00
Tim Abbott
55172e2e0c Remove old zulip_internal nagios_plugins installation. 2016-04-05 13:27:04 -07:00
Tim Abbott
934e8641ee Migrate Zephyr mirror Nagios plugins to subdirectory. 2016-04-05 13:27:04 -07:00
Tim Abbott
7b753e5882 Migrate check_debian_packages to zulip/. 2016-04-05 13:27:04 -07:00
Tim Abbott
2da9fc56d6 Migrate check_pg_replication_lag to zulip/. 2016-04-05 13:27:04 -07:00
Tim Abbott
c2e210ca0d Migrate check_website_response.sh to new zulip::nagios.pp. 2016-04-05 13:27:04 -07:00
Tim Abbott
eb72cecd9e Migrate check_fts_update_log to zulip/. 2016-04-05 13:27:04 -07:00
Tim Abbott
92d696d007 Migrate check_postgres plugins to zulip/. 2016-04-05 13:27:04 -07:00
Tim Abbott
e155ecdc49 Migrate check_rabbitmq plugins to zulip/. 2016-04-05 13:27:04 -07:00
Tim Abbott
3ed7d658f8 Migrate check_send_receive_time to zulip/. 2016-04-05 13:27:04 -07:00
Tim Abbott
ca45ec3f3f Migrate check_email_deliverer plugins to zulip/. 2016-04-05 13:27:04 -07:00
Tim Abbott
4e10424512 Migrate check_worker_memory to zulip/. 2016-04-05 13:27:04 -07:00
Tim Abbott
59b46278be Move check_queue_worker_errors into subdirectory.
This fixes an issue where this worker wasn't even being installed
properly in a way that sets us up for doing further reorganization of
the Zulip Nagios plugins.
2016-04-05 13:27:04 -07:00
Tim Abbott
6f20c43097 Move dependency on nagios_plugins into base.pp. 2016-04-05 13:27:04 -07:00
Eklavya Sharma
05ab57e373 In py3k, exit with 1 if repository is not clean. 2016-04-05 13:22:51 -07:00
Eklavya Sharma
569d1240d0 Exit with 1 in py3k when fixers find an issue.
tools/travis/py3k used to always exit with exit code 0.
It should exit with 1 when fixers detect a compatibility issue.

py3k used [ -z "$failed" ] to check if there was a failure.
This is wrong, since if no failure has occured, failed=0,
and -z checks if a string is of zero length. This commit also
fixes this bug.
2016-04-05 13:22:51 -07:00
Eklavya Sharma
dd501830a6 Change position where git reset is called in py3k.
In py3k, "git reset --hard" was called only if
libmodernize.fixes.fix_dict_six changed files and some of those
changes are not considered false positives by py3k.
But if all of those changes are not considered false positives
by py3k, then "git reset --hard" is not called and the repository
is no longer clean.

This commit fixes this bug.
2016-04-05 13:22:51 -07:00
Eklavya Sharma
5e71777975 Add --no-pager option to git diff in py3k.
This is needed because py3k hangs when a pager opens up.
2016-04-05 13:22:51 -07:00
Eklavya Sharma
adff674b0e tools/travis/py3k now checks all python files.
tools/travis/py3k used to only check files whose names ended with .py.
Now it also checks python scripts which don't have an extension.
It uses tools/lister.py to get a list of all python files.
2016-04-05 13:22:51 -07:00
Eklavya Sharma
ab02ab31e3 Change len(obj.keys()) to len(obj) in tools/get-handlebar-vars.
This prevents libmodernize.fixes.fix_dict_six from reporting
this on running tools/travis/py3k.
2016-04-05 13:22:51 -07:00
Eklavya Sharma
0af154a301 Apply Python 3 futurize transform libfuturize.fixes.fix_absolute_import. 2016-04-05 13:22:51 -07:00
Eklavya Sharma
8a81f8c125 Apply Python 3 futurize transform libfuturize.fixes.fix_print_with_import. 2016-04-05 13:22:51 -07:00
Eklavya Sharma
f4aa609aea Apply Python 3 futurize transform libmodernize.fixes.fix_file. 2016-04-05 13:22:51 -07:00
Eklavya Sharma
be0a4f349d Apply Python 3 futurize transform libmodernize.fixes.fix_filter. 2016-04-05 13:22:51 -07:00
Tim Abbott
78e289f904 Exclude puppet-common tests from puppet linter. 2016-04-04 17:08:29 -07:00
Tim Abbott
e6fb5bb1ea changelog: Add more changes since 1.3.10. 2016-04-03 17:55:06 -07:00
Tim Abbott
75d134a9b2 Import tornado.autoreload explicitly.
Fixes #623.
2016-04-03 16:52:23 -07:00
Ashish
909b0635c8 Add management command to test sending email.
Fixes: #622.

[With cleanups/doc tweaks by tabbott]
2016-04-03 16:50:16 -07:00
Varshit
e0ef1a991e Rewrite sanitize_name to better preserve filenames.
The previous version of sanitize_name dropped all unicode characters
and mangled filenames with multiple `.`s in the extension, leading to
confusing URLs for files uploaded to Zulip.

Fixes #321.

[tweaked significantly by tabbott]
2016-04-03 16:26:12 -07:00
Tim Abbott
4a50336476 Add type: Any for tornado.ioloop.add_timeout return value. 2016-04-03 15:40:24 -07:00
Tim Abbott
4352a022cd Add type: ignore to wildcard imports in pythonrc.py. 2016-04-03 15:40:24 -07:00
Tim Abbott
b6dd6413d0 Add type: ignore for tornado_ioloop_logging Tornado versioning code. 2016-04-03 15:40:24 -07:00
Tim Abbott
53ab18eea0 Add type: ignore for chain.from_iterable. 2016-04-03 15:40:24 -07:00
Tim Abbott
9abd332c07 Add type: ignore for mock.Mock() monkey-patching. 2016-04-03 15:40:24 -07:00
Tim Abbott
0d40473818 Add type: ignore for empty list mypy bug. 2016-04-03 15:40:24 -07:00
Tim Abbott
2c1377319f Add type: ignore for mypy __cmp__ bug. 2016-04-03 15:40:24 -07:00
Tim Abbott
3a2d5266d8 Add type: ignore for mypy super() bug. 2016-04-03 15:40:24 -07:00
Tim Abbott
e3ec3e2526 Add type: ignore for test_helpers monkey-patching. 2016-04-03 15:40:24 -07:00
Tim Abbott
6c999927ac Add type: ignore for csrf_exempt decorator. 2016-04-03 15:40:23 -07:00
Tim Abbott
b7dcf2181f Add PEP-484 type annotations to management commands. 2016-04-03 15:40:23 -07:00
Tim Abbott
b437fe2924 Add PEP-484 annotations to bots/. 2016-04-03 15:40:23 -07:00
Tim Abbott
5d5976e4ae Add PEP-484 type annotations to api/.
It's not clear this is net constructive since it makes our API
bindings harder to install.
2016-04-03 15:40:23 -07:00
Tim Abbott
2d2282ada8 Add PEP-484 type annotations to confirmation/. 2016-04-03 15:40:23 -07:00
Tim Abbott
b8c82d5b43 Add PEP-484 type annotations to analytics/. 2016-04-03 15:40:23 -07:00
Tim Abbott
32f8f85f8b Add PEP-484 type annotations to zerver/*.py. 2016-04-03 15:40:23 -07:00
Tim Abbott
ee8be22160 Add PEP-484 type annotations to zerver/worker/. 2016-04-03 15:40:23 -07:00
Tim Abbott
ec7bb0b011 Add PEP-484 type annotations to zerver/views/. 2016-04-03 15:40:23 -07:00
Tim Abbott
2059f650ab Add PEP-484 type annotations to zerver/lib/. 2016-04-03 15:40:23 -07:00
Tim Abbott
d8f7d89fb4 Add PEP-484 type annotations to zerver/models.py.
Done pair-programming with Guido.
2016-04-03 15:40:06 -07:00
Tim Abbott
b99313545e Add PEP-484 type annotations to global dictionaties. 2016-04-03 15:40:06 -07:00
Tim Abbott
77be524dc4 Add PEP-484 type annotations to generator functions. 2016-04-03 15:40:05 -07:00
Tim Abbott
2c88085572 test_decorators: Clean up Request.REQUEST mocking. 2016-04-03 15:40:05 -07:00
Tim Abbott
70c1b0a01d lister: Add file type detection for node and ruby. 2016-04-01 15:27:32 -07:00
Eklavya Sharma
aead933c14 Add command-line interface to lister.py using argparse. 2016-04-01 15:27:32 -07:00
Eklavya Sharma
81fdeae0ea Remove '.'s from extensions in lister.py interface and lint-all.
In lint-all, change occurences of '.py', '.js', ... to 'py', 'js', ....
2016-04-01 15:27:16 -07:00
Eklavya Sharma
ad4c20a3e6 Migrate lint-all to lister.py for getting files.
This has the side effect of making lint-all check all shell scripts,
not just those under scripts/, tools/, and bin/.

[commit message expanded by tabbott]
2016-04-01 15:24:43 -07:00
Eklavya Sharma
ec8ae1f4c5 Add option to determine file type in lister.py using shebang. 2016-04-01 15:19:13 -07:00
Eklavya Sharma
5063f16f82 Add options for file type in lister.py
Add option to filter files by their extension.
Add option to return a dict of list of files keyed by their file type.
2016-04-01 15:19:13 -07:00
Eklavya Sharma
81aabb5831 Add option to exclude files from lister.py. 2016-04-01 15:19:13 -07:00
Eklavya Sharma
422fef2e24 Add argument modified_only to lister.py.
Add option of only showing modified files in tools/lister.py.
2016-04-01 15:19:13 -07:00
Eklavya Sharma
6954eb072c Create lister.py.
Make module tools/lister.py which lists all files in a directory
tracked by git.  This is done because lister.py will be used by other
scripts in the future which have to introspect files in the repository,
like linters, static code checkers, etc.
2016-04-01 15:19:13 -07:00
Tim Abbott
5c810ad0bc Add new markdown documentation to ReadTheDocs. 2016-04-01 14:57:30 -07:00
Tim Abbott
a1683b1eaf Reorganize postgres docs. 2016-04-01 10:01:07 -07:00
Tim Abbott
52764763c6 Add some docs on schema migrations. 2016-04-01 09:57:00 -07:00
Tim Abbott
94cca8b758 Expand documentation on postgres with Zulip. 2016-04-01 09:56:59 -07:00
Tim Abbott
37b79deb60 Expand documentation on frontend build process. 2016-04-01 09:56:59 -07:00
Tim Abbott
7a671c2652 Add documentation on the Zulip RabbitMQ queues. 2016-04-01 09:56:59 -07:00
Tim Abbott
96eb81e5d5 Add documentation of Zulip's markdown implementation. 2016-04-01 09:56:59 -07:00
Tim Abbott
82831231b5 Document postgres vacuuming alert and health check command. 2016-04-01 09:56:59 -07:00
Tim Abbott
342b4eb457 Add a detailed integration writing guide.
Fixes: #70.
2016-04-01 09:56:59 -07:00
Tim Abbott
7d74c64f75 Add hackish tool for finding unused CSS. 2016-04-01 09:34:46 -07:00
Tim Abbott
b8c7cfb77e docs: Add Javascript manual testing section. 2016-04-01 09:34:45 -07:00
Tim Abbott
a407f090e1 models: Document some of the more interesting model classes. 2016-04-01 09:34:45 -07:00
Ryan Moore
2fe0700f55 Update memcache -> remote cache in inline documentation. 2016-03-31 12:56:42 -07:00
Ryan Moore
beac606ce6 switch output stats memcached -> remote_cache 2016-03-31 12:54:29 -07:00
Ryan Moore
15cc3fde7b s/memcache/remote_cache/ in models 2016-03-31 12:54:29 -07:00
Ryan Moore
1489f0992c s/fill_memcached_cache/fill_remote_cache/g 2016-03-31 12:54:29 -07:00
Ryan Moore
18139fd86f s/memcached_prefix/remote_cache_prefix/g 2016-03-31 12:54:29 -07:00
Ryan Moore
85b05d4e2b s/memcached_output/remote_cache_output/g 2016-03-31 12:54:29 -07:00
Ryan Moore
5346e2ac23 s/memcached_count_delta/remote_cache_count_delta/g 2016-03-31 12:54:29 -07:00
Ryan Moore
1f120aa3a8 s/memcached_total/remote_cache_total/g 2016-03-31 12:54:29 -07:00
Ryan Moore
3c180b43df s/memcached_stats/remote_cache_stats/g 2016-03-31 12:54:28 -07:00
Ryan Moore
1a2117292f s/memcached_requests/remote_cache_requests/g 2016-03-31 12:54:28 -07:00
Ryan Moore
16c936f638 s/memcached_time/remote_cache_time/g 2016-03-31 12:54:28 -07:00
Ryan Moore
9f29b80f8a s/items_for_memcached/items_for_remote_cache/g 2016-03-31 12:54:28 -07:00
Tim Abbott
93b3feda43 Fix node test broken by reloading changes. 2016-03-30 23:40:00 -07:00
Tim Abbott
723d8c288a Add tornado autoreload hook to dump event queues on reload.
It's always been the case that in production, Tornado dumps all the
event queues when shut down so that they can be reloaded by the
replacement Tornado process.  This never worked in development because
the codepath for auto-reload didn't go through either a signal or
sys.exit (it re-execs the process instead).

This meant that we didn't have a mechanism for testing the event queue
dump/load functionality in the development environment.  We fix this
by adding such dumping/loading.  However, this breaks the automatic
reloading of open browser windows on a server restart, so we add that
back in by adjusting the special `restart` events to pass a special
`immediate` flag when used in development.

This also has the benefit of removing the "Bad event queue" errors one
would get on every file save induced restart on the Python console.
2016-03-30 23:09:16 -07:00
Tim Abbott
6d2ae9abbc Fix cleanup_event_queue being called multiple times during reload.
Apparently it isn't always the case that removal of jquery and the DOM
prevents cleanup_event_queue from being called via the postunload
hook, so add a check to avoid it being double-called.
2016-03-30 23:09:16 -07:00
Tim Abbott
59e2be2f5f Notify clients when an event queue is garbage collected. 2016-03-30 23:09:14 -07:00
Tim Abbott
44ed90db85 Fix get_events restarting during an ongoing reload.
Previously, the browser might restart a get_events operation even
while it was in the middle of executing a `DELETE /events` query to
cause its event queue to be de-allocated.  This was a rare race
condition when we weren't notifying clients when event queues were
de-allocated, but this will become a common case in the next commit.
2016-03-30 22:28:07 -07:00
Tim Abbott
bf43db0dad Send responses from fetch_events in dict format. 2016-03-30 22:04:09 -07:00
Tim Abbott
485e46f136 Wrap most of fetch_events in a try/except JsonableError.
This is a no-op right now, but we'll want the new structure for the
next commit, and splitting this out makes it a lot easier to read what
is actually changed in the next commit.
2016-03-30 22:02:32 -07:00
Tim Abbott
ad1494f8e0 Use dictionaries for passing data into event server subsystem.
This marshalls data in a portable format in preparation for splitting
the event queue server from the Tornado server.
2016-03-30 22:01:08 -07:00
Tim Abbott
6cd14af18f events: Migrate client_type => client_type_name in marshalling code. 2016-03-30 22:00:50 -07:00
Tim Abbott
d936bf61f9 Exclude 'from typing import *' from linter. 2016-03-30 21:50:31 -07:00
Tim Abbott
8c0b110e9a Add python-typing dependency. 2016-03-30 21:50:25 -07:00
Tim Abbott
e9637a545f Rename zerver.handlers to zerver.logging_handlers for clarity. 2016-03-30 21:48:38 -07:00
Tim Abbott
10777c85d4 Move npm install to end of development installation process.
This should make it easier for users to recover from failed `npm
install` commands when setting up their environment.
2016-03-29 21:56:19 -07:00
Ashish
62cb36c9e0 Add proxy notes to new README.dev.md troubleshooting section.
[with some fixups by tabbott]
2016-03-29 21:54:05 -07:00
Tim Abbott
c16749d783 Add missing dependency on netcat in both dev and prod.
Fixes #474.
2016-03-29 21:30:48 -07:00
Tim Abbott
d8493b071b Fetch tsearch_extras packages from GitHub dist repository. 2016-03-29 21:25:33 -07:00
Tim Abbott
970d697e88 Fetch PhantomJS packages from a GitHub URL.
This should fix a problem we've been having with errors downloading
the PhantomJS packages from their original hosting service.

Eventually we should move it to an S3 bucket.
2016-03-29 21:24:38 -07:00
goelakash
36cf398ec3 Factor out phantomjs download script for use in by-hand instructions.
Also, modify README.dev.md to document need to run this script to
download PhantomJS.

Fixes #499.

[cleaned up a bit by tabbott]
2016-03-29 20:10:17 -07:00
Eklavya Sharma
20f4bcd86e Delete tools/python-proxy since it is not used. 2016-03-29 20:02:26 -07:00
Ashish
2050f5c7fa Add API example for fetching historical messages.
Fixes: #269.
2016-03-29 19:04:43 -07:00
akashnimare
41c0b92668 Optimize images on landing page to improve load performance. 2016-03-29 18:26:29 -07:00
SummerBulb
d3d9dc1557 README.prod.md - fix botsq typo. 2016-03-29 11:15:53 -07:00
goelakash
25a75bcefe Change LOCAL_UPLOAD_DIR to 'uploads' in development.
Fixes #488.
2016-03-29 11:12:29 -07:00
Tim Abbott
2adf6d822f puppet: Fix process_queue command lines to use the new argument style.
cd2348e9ae broke installing Zulip in
production since it didn't correctly update the puppet configuration
to call the process_queue script using the new argument format.

This commit isn't ideal in that I'd prefer to not require updating
puppet in sync with the actual running code, but we don't have a great
mechanism for doing that.

Fixes #586.
2016-03-27 23:17:16 -07:00
Tim Abbott
06b33da709 process_queue: Fix missing worker.setup() in single-threaded codepath. 2016-03-27 23:17:16 -07:00
Kunal Gupta
6d0e868897 README.prod.md: Fix installation instructions to not hardcode version.
Previously, we needed to update the installation instructions with the
current version of Zulip in production every time we did a release,
which was kinda a pain (and hadn't happened since 1.3.6).

Fixes #576.

[commit message details expanded by tabbott]
2016-03-27 13:55:49 -07:00
Kartik Maji
2b3312cd6e Update unread counts for streams when muting topics.
Fixes #427.
2016-03-27 13:49:52 -07:00
SummerBulb
cc118824d5 README.prod.md: Fix a formatting typo. 2016-03-27 13:20:24 -07:00
Vladislav Manchev
294030ca04 Fix Travis failures due to redirects when downloading PhantomJS. 2016-03-23 21:54:56 -07:00
Zev Benjamin
965f923ac3 Remove postgres2 configuration 2016-03-23 20:41:42 -07:00
Zev Benjamin
ae2560a027 Add postgres3 configuration 2016-03-23 20:41:25 -07:00
Tim Abbott
6137ae9902 Fix incorrect shell quoting in check_worker_memory. 2016-03-23 20:40:06 -07:00
Tim Abbott
210c2897e7 Fix check_worker_memory regular expression. 2016-03-23 20:40:02 -07:00
Tim Abbott
9607144bf2 Fix off-by-one error in presence list updating logic.
The original logic for incremental presence list updating from
668d0d9dfa incorrectly attempted to
insert the user 1 spot later than its proper index in the listing.
2016-03-21 20:28:05 -07:00
Tim Abbott
29b8d71871 Remove throttling of presence updates coming from server events.
Now that we're doing presence updates in a performant fashion, we
don't need to throttle processing these events, and in fact the
throttling of these events created a correctness problem, since we're
now doing incremental updates rather than just rerendering everything
after each event.
2016-03-21 20:28:05 -07:00
Tim Abbott
4bb48abc0d Fix quoting bug in user presence update function.
The code in 668d0d9dfa for removing an
existing user from the user list to update the status didn't correctly
quote the email address of the user in its jquery selector.
2016-03-21 20:28:05 -07:00
Luke Faraone
5c28b0340a Don't show Zulip.com terms on other sites
While we already don't link to /terms anywhere on the site, they can still be
accessed if you navigate to /terms directly. Now, those routes will only be
exported on the Zulip.com service.

We should ideally provide a mechanism for deployments to specify their own
terms without modifying source code; in the interim, sites that have already
customised the provided Zulip.com terms can simply carry a patch reverting this
commit.
2016-03-21 05:46:28 +00:00
Luke Faraone
85d2e8d249 Add missing periods to some bullet point headings 2016-03-21 05:42:40 +00:00
Luke Faraone
27e346302c Consistently use ':'s when describing bugs in README.md. 2016-03-21 03:51:50 +00:00
Luke Faraone
b92e829d94 Fix incorrect dash used in non-Python dependency list. 2016-03-21 03:51:50 +00:00
Luke Faraone
1b4d8542a0 Minor edit to non-Python dependency description 2016-03-21 03:51:50 +00:00
Luke Faraone
d93a2bcf11 Reformat README.*.md to 70 characters uniformly.
There are some stragglers, but this is better than the previous SOTW
where line length was all over the place.
2016-03-21 03:51:49 +00:00
Tim Abbott
cd2348e9ae Run queue processers multithreaded in development.
This change drops the memory used for Python processes run by Zulip in
development from about 1GB to 300MB on my laptop.

On the front of safety, http://pika.readthedocs.org/en/latest/faq.html
explains "Pika does not have any notion of threading in the code. If
you want to use Pika with threading, make sure you have a Pika
connection per thread, created in that thread. It is not safe to share
one Pika connection across threads.".  Since this code only connects
to rabbitmq inside the individual threads, I believe this should be
safe.

Progress towards #32.
2016-03-20 18:04:24 -07:00
Tim Abbott
49b55af9cd Fix trello-to-zulip URL.
The original author had deleted his repository.
2016-03-20 17:53:43 -07:00
Alexander Pushin
888f53de13 Fix collapsing messages in narrowed views.
First user-fasing problem is that when user click to "Collapse" button
of message from narrowed list, buttons "Uncollapse" and "[More...]" does
not work. Second, is that when user collapse/uncollapse some message
from narrowed list, the collapsing/uncollapsing of the same message in
home list does not work in appropriate way.

In "popovers.js" there is the function that is called on click to the
buttons "Collapse" or "Un-collapse". It should show and hide body of a
message. If a message list is narrowed, it should show/hide message in
home list too. So, the first problem is that "toggle_row()" in this
function call methods "collapse(row)" or "uncollapse(row)" from
"condense.js" twice (for row and home_row) using condition
"if (message.collapsed)". When it happen the first time, the variable
"message.collapsed" is changed. That is why next call of "toggle_row()"
work incorrectly.

The second problem is that the function in "condense.js" that is
called on click to the button "[More...]" contains no code for
collapsing/uncollapsing message from home list. It just calls
"collapse(row)" or "uncollapse(row)" for row from narrowed list.

Now, functions "collapse(row)" and "uncollapse(row)" get row from
current list and change both messages (from current list and home
list). On-click functions call them just once for making all of needed
message changes. So, when user collapse or uncollapse message from
home or narrowed list it works correctly.

Fixes: #516
2016-03-20 16:58:57 -07:00
Tim Abbott
06e68d52ce Remove dead AsyncDjangoHandler field. 2016-03-20 16:53:13 -07:00
Tim Abbott
0419430000 Enhance Tornado logging with Handler stats.
This was useful data for debugging handler/memory leaks.
2016-03-20 16:53:13 -07:00
Tim Abbott
e51811aa9e Add comment documenting logic flow of fetch_events. 2016-03-20 16:53:13 -07:00
Tim Abbott
7fabfe9cb9 Add __repr__s for ClientDescriptor and AsyncDjangoHandler. 2016-03-20 16:53:13 -07:00
Tim Abbott
0b96e5e43f Fix connection not being closed cleanly on event queue GC.
Apparently, our event queue garbage collection logic never actually
disconnected any existing handler objects.

We fix this by disconnecting the handler inside cleanup(), adding a
special check to avoid creating a pointless timeout object.
2016-03-20 16:53:13 -07:00
Tim Abbott
3f55e26a9f Fix Tornado memory leak with synchronously handled requests.
The new Tornado handler tracking logic properly handled requests that
threw an exception or followed the RespondAsynchronously code path,
but did not properly de-allocated the handler in the syncronous case.

An easy reproducer for this is to load a new Zulip browser window;
that will leak 2 handler objects for the 2 synchronous requests made
from Django to Tornado as part of initial state fetching.
2016-03-20 16:53:13 -07:00
Tim Abbott
12a5a3a6e1 Actually fix main Tornado memory leak of handler objects.
This line appears to have been lost in rebasing from the original
implementation of 1396eb7022faec4c2d91553800a35781a96dd5bd; so the
previous fix actually only addressed the issue in a rare exception
case.
2016-03-20 16:53:12 -07:00
Vladislav Manchev
7aab17d0c0 Add several major Hubot integrations to integration docs.
Related to #335.
2016-03-20 11:50:21 -07:00
Tim Abbott
320428052a Fix AsyncDjangoHandler view exception code path.
The recent Tornado memory leak fix
(1396eb7022) didn't use the correct
variable name for the current handler ID, causing this cleanup code to
fail in the event that a view raised an exception.
2016-03-19 22:50:35 -07:00
Eklavya Sharma
9e3c3e14f5 Partially apply Python 3 libmodernize.fixes.fix_dict_six.
Refer to #256
2016-03-19 15:52:58 -07:00
Eklavya Sharma
176c507b0a Removed calls to ifilterfalse.
Replaced calls to ifilterfalse by list comprehensions because
ifilterfalse is not part of python 3.  Also changed some lists to sets
for faster lookup.

Refer to #256.
2016-03-19 15:46:31 -07:00
Eklavya Sharma
851b0a871d Fix python 3 decode error in zerver/tests.py.
Refer to #256.
2016-03-19 15:46:18 -07:00
Eklavya Sharma
186efc6a6d Handle unicode properly in camo.
In Bugdown's InlineHttpsProcessor.run, hex_encoded_url was not being
decoded to utf-8. This was causing problems in python 3.

Refer to #256.
2016-03-19 15:46:07 -07:00
Tim Abbott
f9222de83e Auto-load commonly used modules in manage.py shell.
This automatically loads settings, zerver.models.* and
zerver.lib.actions.* when you start `manage.py shell`, which should
save a bit of time basically every time someone uses it.

Fixes #275.
2016-03-19 11:32:49 -07:00
Josh Mandel
b06739df11 Move email digest triggering to default zulip config.
Previously, even though the Zulip digest emails were documented in the
settings, the cron job to run the script that actually sends the daily
digest emails wasn't included in the non-zulip.com part of the Zulip
production distribution.  The overall consequence is that digest
emails didn't work for non-zulip.com users.  This fixes that issue by
moving that cron job into the zulip manifests.

[commit message details expanded by tabbott]
2016-03-19 10:34:41 -07:00
Tim Abbott
02ccb68f7e code style: Document auto-closing GitHub issues. 2016-03-19 10:17:25 -07:00
Tim Abbott
ecc66d6eec code style: Improve the commit message style documentation. 2016-03-19 10:09:21 -07:00
Varshit
72033069ed Extend lint-all to check for newlines at the end of files. 2016-03-17 23:03:56 -07:00
Tim Abbott
3a46bae542 Fix shell script list computation to exclude files not in git.
This fixes an issue where the next commit's no-newline-and-end-of-file
check was incorrectly firing on tools/phantomjs.
2016-03-17 23:03:56 -07:00
Anindya Chakravarti
d72b8b83f7 Fixed a typo in documentation. 2016-03-17 22:53:42 -07:00
Tim Abbott
1396eb7022 Fix Tornado memory leak of handler objects.
In 2ea0daab19, handlers were moved to
being tracked via the handlers_by_id dict, but nothing cleared this
dict, resulting in every handler object being leaked.  Since a Tornado
process uses a different handler object for every request, this
resulted in a significant memory leak.  We fix this by clearing the
handlers_by_id dict in the two code paths that would result in a
Tornado handler being de-allocated: the exception codepath and the
handler disconnect codepath.

Fixes #463.
2016-03-17 18:33:59 -07:00
Vladislav Manchev
753ccf67b1 Fix regression when saving organization settings on administration page.
Saving the organization settings form in the administration did not
work due to a trivial form name mismatch caused by following
revisions: 472898c and 58aba59.
2016-03-17 18:29:04 -07:00
Tomasz Kolek
3e3a224607 Moved pagerduty webhook into its own file pagerduty.py 2016-03-14 20:44:50 -07:00
Tomasz Kolek
05dce01cee Moved travis webhook into its own file travis.py 2016-03-14 20:44:45 -07:00
Tomasz Kolek
f640470fa4 Moved zendesk webhook into its own file zendesk.py 2016-03-14 20:44:42 -07:00
Tomasz Kolek
b3e5a256f5 Moved freshdesk webhook into its own file freshdesk.py 2016-03-14 20:44:41 -07:00
Tomasz Kolek
021c66fd9a Moved stash webhook into its own file stash.py 2016-03-14 20:44:41 -07:00
Tomasz Kolek
7a4c9d243f Moved deskdotcom webhook into its own file deskdotcom.py 2016-03-14 20:44:41 -07:00
Tomasz Kolek
087bd72814 Moved bitbucket webhook into its own file bitbucket.py 2016-03-14 20:44:41 -07:00
Tomasz Kolek
93b52f6f8e Moved newrelic webhook into its own file newrelic.py 2016-03-14 20:44:41 -07:00
Tomasz Kolek
a2b31da045 Moved pivotal webhook into its own file pivotal.py 2016-03-14 20:44:41 -07:00
Tomasz Kolek
5ade895936 Moved jira webhook into its own file jira.py 2016-03-14 20:44:41 -07:00
Tomasz Kolek
a0512244b3 Moved beanstalk webhook into its own file beanstalk.py 2016-03-14 20:44:41 -07:00
Tomasz Kolek
6a3ab0605d Moved github webhook into its own file github.py 2016-03-14 20:44:41 -07:00
Tomasz Kolek
8a0ed47751 moved webhooks to python package 2016-03-14 20:44:41 -07:00
Tim Abbott
b3f731e2b5 Update documentation on Frontend buddy list performance. 2016-03-13 10:33:23 -07:00
Tim Abbott
307f25308c provision: Add support for 32-bit x86 platform.
The only places we use the architecture were for finding the
tsearch_extras and phantomjs binaries; Luke Faraone kindly uploaded
both 32-bit and 64-bit binaries for tsearch_extras 0.1.3, so with a
bit of refactoring, we can now support 32-bit.

Fixes #505.
2016-03-13 10:11:19 -07:00
Eklavya Sharma
37f9520666 Make the remaining ambiguous divisions python 3 compatible.
Refer to #256
2016-03-12 10:53:51 -08:00
Eklavya Sharma
14130a84ca Partially apply Python 3 transform libpasteurize.fixes.fix_newstyle
Refer to #256
2016-03-12 23:19:56 +05:30
Josh Mandel
d3d044ba00 Don't hide the streams gear menu.
The previous behavior of only showing it on hover was not discoverable
enough.
2016-03-12 09:28:12 -08:00
Eklavya Sharma
3ab567db98 Add call to generate-fixtures in test-backend.
Add call to tools/generate-fixtures in tools/test-backend before
starting the tests.  Previously, test-backend could fail if called
after tools/test-js-with-casper had failed.

Fixes #501.
2016-03-12 09:24:32 -08:00
Eklavya Sharma
01bfa2d94d Apply Python 3 futurize transform libmodernize.fixes.fix_unicode_type
Refer to #256
2016-03-10 22:04:15 -08:00
Eklavya Sharma
b9e792c4e6 Apply Python 3 futurize transform libmodernize.fixes.fix_xrange_six
Refer to #256
2016-03-10 22:03:58 -08:00
Eklavya Sharma
aa505b0d55 Apply Python 3 futurize transform libmodernize.fixes.fix_map
Refer to #256
2016-03-10 22:03:44 -08:00
Eklavya Sharma
7b8cb105bf Apply Python 3 futurize transform libmodernize.fixes.fix_imports_six
Refer to #256
2016-03-10 22:03:30 -08:00
Eklavya Sharma
def027a1ec Apply Python 3 futurize transform libmodernize.fixes.fix_filter
Refer to #256
2016-03-10 22:03:06 -08:00
Eklavya Sharma
d3b63f9a2d Apply Python 3 futurize transform libmodernize.fixes.fix_file
Refer to #256
2016-03-10 22:02:34 -08:00
Eklavya Sharma
e83a2c8cc2 Apply Python 3 futurize transform libmodernize.fixes.fix_basestring
Refer to #256
2016-03-10 22:02:27 -08:00
Eklavya Sharma
1941201075 Apply Python 3 futurize transform libfuturize.fixes.fix_raise
Refer to #256
2016-03-10 22:02:22 -08:00
Eklavya Sharma
c59185e119 Apply Python 3 futurize transform libfuturize.fixes.fix_print_with_import
Refer #256
2016-03-10 22:02:17 -08:00
Eklavya Sharma
3e7827358e Apply Python 3 futurize transform libfuturize.fixes.fix_next_call 2016-03-10 22:02:12 -08:00
Eklavya Sharma
e2d5ec1868 Apply Python 3 futurize transform lib2to3.fixes.fix_ws_comma 2016-03-10 22:02:04 -08:00
Eklavya Sharma
4fb549abe8 Apply Python 3 futurize transform lib2to3.fixes.fix_idioms
Refer to #256
2016-03-10 22:02:01 -08:00
Eklavya Sharma
ab7287474e Apply Python 3 futurize transform lib2to3.fixes.fix_has_key
Refer to #256
2016-03-10 22:01:55 -08:00
Eklavya Sharma
f3d387e727 Apply Python 3 futurize transform lib2to3.fixes.fix_except 2016-03-10 22:01:50 -08:00
Kartik Maji
e804185ae6 Fix subscription button in notification bot announcements.
Fixes #456.
2016-03-09 20:36:42 -08:00
Kartik Maji
3bf54e7da7 Fix opacity for muted topics within muted streams in left sidebar.
Fixes #428.

[Comment and commit message tweaked by tabbott]
2016-03-09 10:28:19 -08:00
Eklavya Sharma
4ec0d76586 Fixed a typo in README.dev.md 2016-03-08 09:19:26 -08:00
Tim Abbott
df0d2a726d python3: Add missing utf-8 encoding/decoding in various places. 2016-03-08 09:14:15 -08:00
Tim Abbott
a46647a87a python3: Mark certain strings as unicode strings.
This is required in Python 3 since these strings are combined with
other unicode strings.
2016-03-08 09:14:11 -08:00
Karambir Singh Nain
fc0a414fe6 Fixed markdown links in README and README.dev
[Patched with some tweaks by tabbott]
2016-03-08 08:44:44 -08:00
Luke Faraone
c8de86894f Add Travis CI button to README.md 2016-02-21 00:47:04 +00:00
Luke Faraone
9d9bfb27ef Correct shell quoting around $DEFAULT_USER in terminate-psql-sessions
Previously, we used shell quoting that would result in the shell variable not
being substituted. Instead, we use `"`s that will allow for variable
substitution.
2016-02-19 02:09:50 +00:00
Luke Faraone
c89d675462 Add missing wget dependency to provision.py
We also explicitly include `ca-certificates`, as it is needed for the install
to complete. Usually this is brought in as a `Recommends` of `wget`, but some
systems may not automatically include such dependencies.

Fixes #470.
2016-02-18 03:41:16 +00:00
Vladislav Manchev
668d0d9dfa Fix performance issues with user presence list in large realms.
Whenever a user became active, this triggers an immediate presence
update event (to show that user as active).  The implementation for
that event (running on the browsers of all other users in the realm)
would fully rerender the presence list, which can be an expensive
operation in a large realm, just to update the status for that one
user.  This fixes that case to just remove the user from the list and
then re-insert it at the appropriate index.

[Commit message expanded with more details by Tim Abbott]
2016-02-12 20:04:43 -08:00
Tim Abbott
784a662707 Fix documentation around iOS not supporting a custom server. 2016-02-09 21:19:57 -08:00
Tim Abbott
3a6889e19f Vagrantfile: Don't error on platforms where lxc-ls doesn't exist.
Apparently vagrant executes the configuration code for all providers,
not just the one that's actually selected.

Fixes #461.
2016-02-07 21:11:05 -08:00
Tim Abbott
cbf9b7605a Add test that all functions defined in urls.py actually exist.
This would have caught the create_user_backend issue introduced recently.
2016-02-07 19:21:53 -08:00
Reid Barton
6c6dc1d81d Allow create user API to create any user in an open realm. 2016-02-07 19:19:19 -08:00
Reid Barton
9735025167 Refactor logic around restricted_to_domain.
Add a function email_allowed_for_realm that checks whether a user with
given email is allowed to join a given realm (either because the email
has the right domain, or because the realm is open), and use it
whenever deciding whether to allow adding a user to a realm.

This commit is not intended to change any behavior, except in one case
where the Zulip realm's domain was not being converted to lowercase.
2016-02-07 10:54:52 -05:00
Reid Barton
0755b51c2e Move create_user_backend into zerver.views.users.
Commit aa33a0da moved users views into their own file, but missed this one.
2016-02-07 10:54:48 -05:00
Reid Barton
4e5f18407d Add comment in create_user_backend about not needing to invite users first. 2016-02-07 10:52:50 -05:00
Tim Abbott
d05bdbd919 Document letsencrypt in the SSL instructions. 2016-02-03 20:05:40 -08:00
Tim Abbott
34cf1f55bf Link to nginx certificate chaining documentation in SSL docs.
Fixes #430.
2016-02-03 20:05:40 -08:00
Tim Abbott
1af7cbfd64 runtornado: Move more imports to the top of the file.
This is needed for adding more specific type annotations but is
otherwise counterproductive since it increases the diff from the
original.
2016-02-03 19:47:14 -08:00
Tim Abbott
2259ce62f8 tornado: Fix AsyncDjangoHandler get() and friends missing args/kwargs. 2016-02-03 19:47:14 -08:00
Tim Abbott
1d008576f2 QueueProcessingWorker: Add stub consume function and queue_name.
QueueProcessingWorker will now throw errors if there's a misconfigured
queue processor.
2016-02-03 19:47:14 -08:00
Tim Abbott
3469fd4bb2 Fix last remaining use of file() -> open(). 2016-02-03 19:31:46 -08:00
Tim Abbott
d7b7ae2d0f rundjango: Fix confusing return line in log_message_monkey. 2016-02-03 19:31:45 -08:00
Tim Abbott
05a40f11b3 runtornado: Add explicit return None. 2016-02-03 19:31:45 -08:00
Tim Abbott
5f9cd4d7c8 populate_db: Refactor saved_data to use a consistent type. 2016-02-03 19:29:45 -08:00
Tim Abbott
c55ac01ae6 populate_db: Rename confusing sub local variable.
While the code was technically correct, elsewhere in that function sub
a Subscription object, not a tuple of ints.
2016-02-03 19:29:45 -08:00
Tim Abbott
37e987e250 Make realm_filters --show's empty list output more consistent. 2016-02-03 19:29:44 -08:00
Tim Abbott
3475a5c1ed Fix test_runner.py fast_test_only return type. 2016-02-03 19:29:44 -08:00
Tim Abbott
fcc32b1093 Fix check_redis abuse of setting self.trim global. 2016-02-03 19:29:44 -08:00
Tim Abbott
693b9110df Fix weird import of get_user_profile_by_email from decorator.py. 2016-02-03 19:29:44 -08:00
Tim Abbott
a2ef1642d1 Fix buggy report_error return value. 2016-02-03 19:29:44 -08:00
Tim Abbott
7595e4b05f process_queue: Fix worker variable being accessed before initialization. 2016-02-03 19:29:44 -08:00
Tim Abbott
b34768837d storage: Fix type error returning nothing in dry run case. 2016-02-03 19:29:44 -08:00
Tim Abbott
1ee0706511 Fix missing UTF-8 encoding. 2016-02-03 19:29:30 -08:00
Tim Abbott
df4ab3c788 run_dev: Fix return for twisted finish function.
This was flagged by mypy; it's not clear this should be needed.
2016-02-03 19:29:07 -08:00
Tim Abbott
10f15a2d00 middleware: Fix str/unicode type mismatch in statsd_path. 2016-02-03 19:29:07 -08:00
Tim Abbott
2436ad19ba analytics: Cleanup confusingly type-variable all_records. 2016-02-03 19:29:07 -08:00
Tim Abbott
23705f4f16 Remove duplicate self._log_data initialization. 2016-02-03 19:29:07 -08:00
Tim Abbott
df1670ef59 Fix various float initialization to use 0.0 instead of 0.
This is needed to type-check these values.
2016-02-03 19:29:07 -08:00
Tim Abbott
999e4688d4 Fix missing return None in ZulipRemoteUserBackend.authenticate. 2016-02-03 19:29:07 -08:00
Tim Abbott
fc02ea9f67 do_add_default_stream: Fix return value if stream exists.
Discovered using mypy static type checker.
2016-02-03 19:28:48 -08:00
Tim Abbott
ff3555734d provision: Return success from main function. 2016-02-03 19:25:19 -08:00
Tim Abbott
620411c0ea Fix type mismatches in streams.py. 2016-02-03 19:25:19 -08:00
Tim Abbott
e6e2584c5a test_runner: Cleanup fast_tests_only types. 2016-02-03 19:25:19 -08:00
Tim Abbott
ee6062691a Fix missing None in check_none_or return. 2016-02-03 19:25:18 -08:00
Tim Abbott
f03bfc5816 Fix missing prefix cache error output. 2016-02-03 19:25:18 -08:00
Tim Abbott
8654b57c7b bugdown: Move definition of current_node above set_text function using it. 2016-02-03 19:25:18 -08:00
Tim Abbott
eee36618fe run-dev: Fix overwritten manage_args variable with the wrong type.
manage_args is set to a list of arguments a few lines later in the
function, making this initialization as the empty string useless and
confusing.

Discovered using mypy.
2016-02-03 19:25:18 -08:00
Tim Abbott
8dcdb1d8a8 actions: Remove duplicate import of RealmFilter.
Apparently we were importing it twice in the same import statement.

Discovered using mypy.
2016-02-03 19:25:17 -08:00
Tim Abbott
294b7aa7bd EventsRegisterTest: Remove unused variable maxDiff.
Discovered using mypy.
2016-02-03 19:25:17 -08:00
Tim Abbott
e9f39922a0 notify_subscriptions_*: Fix use of leaked stream variable.
While I believe this actually produced correct output since users are
always subscribed to streams within their realm, this code was
definitely wrong.

Discovered using the mypy type-checking tool.
2016-02-03 19:25:17 -08:00
Tim Abbott
6c5cee2400 Cleanup 500s due to Google oauth2 errors.
These are user errors, albeit somewhat interesting ones, so they
should be logged (and return a user error response), not throw an
exception.
2016-02-02 23:08:20 -08:00
Tim Abbott
aad3bff193 Harden style rule for % comprehensions and fix existing errors. 2016-02-02 23:08:19 -08:00
Zev Benjamin
4887a79d21 Make the nginx log directory owned by zulip
This is required to make log2zulip not error out when reading the nginx
error.log.
2016-02-02 23:05:44 -08:00
Zev Benjamin
e780f5dab5 Make log2zulip error message more accurate.
In particular, in the case of a permissions issue reading the log
file, it would claim the log file doesn't exist.
2016-02-02 23:04:48 -08:00
Tim Abbott
206dc3aafc Add python 3 compatibility check for libmodernize.fixes.fix_dict_six.
It's not clear whether this will end up being net negative in value in
the long term since it's kinda hard to understand the output, but in
the short term it should prevent regressions.
2016-01-26 21:11:25 -08:00
Tim Abbott
5bacda3662 python3: Fix usage of .keys()/.values() to handle iterators.
This fixes the places where we use the result of .keys(), .items(),
and .values() that wouldn't work with an iterator to wrap them with
list().
2016-01-26 21:11:25 -08:00
Tim Abbott
f5de149976 python3: specify explicit sorting algorithm for subscriptions. 2016-01-26 21:11:25 -08:00
Tim Abbott
05a827c520 need_to_render_content: Fix comparison with None.
If the content wasn't rendered, both rendered_content and
rendered_content_version would be None.  In addition to being
confusing, in Python 3, `None < 2` is an error and this code breaks.
2016-01-26 21:11:25 -08:00
Tim Abbott
bd0918cd5a python3: Use zip from the six.moves package. 2016-01-26 21:11:24 -08:00
Tim Abbott
757e89260e Migrate use of StringIO to Python 2+3 compatible six.moves.cStringIO.
And add a check for additional usage of the original StringIO module.
2016-01-26 21:09:43 -08:00
Tim Abbott
1f44417fc1 Switch to using Python 3 style division everywhere.
Also add testing for this to our Python 3 compatibility test suite.
2016-01-26 21:09:43 -08:00
Tim Abbott
6528b18ad3 Switch all urllib/urlparse usage to six.moves.urllib.
This provides Python 2+3 compatibility for our use of urllib.

Also add a test to avoid future regressions.
2016-01-26 21:09:43 -08:00
Tim Abbott
52f9574047 Fix missing python-six dependency for Zulip API. 2016-01-26 21:09:42 -08:00
Tim Abbott
700055c194 Apply modernize transform libmodernize.fixes.fix_file.
This replaces use of file() with open() which is python 3 compatible,
and also adds it to our python 3 support test suite.
2016-01-26 21:09:42 -08:00
Tim Abbott
83dd51dcd6 Remove now-obsolete get_message_by_id_dbwarn transition code. 2016-01-26 21:05:11 -08:00
Tim Abbott
eecd1513b3 Don't access/store full Client objects in Tornado queue servers.
At present, we only do a few simple checks on the client type inside
the event system, and this saves database/memcached queries.

Note that this preserves the structure of the marshalled name in
to_dict/from_dict as client_type to avoid an unnecessary migration.
2016-01-26 21:04:32 -08:00
Tim Abbott
e3b6bfa3ca Remove code for pickle event queue store.
Since we've had the JSON store in all environments for some time, we
no longer need this legacy code.
2016-01-26 20:59:34 -08:00
Tim Abbott
f6073d1708 Move HTTPResponse construction out of event_queue.py. 2016-01-26 20:59:19 -08:00
Tim Abbott
a9bf4b4cc7 Store the client name for the current handler in ClientDescriptor. 2016-01-26 20:58:01 -08:00
Tim Abbott
c7e3c3ce38 Look up client descriptors by handler_id.
Previously, client descriptors were referenced directly from the
handler object.  Once we split the Tornado process into separate queue
and connection servers, these will no longer be in the same process,
so we need to reference them by ID instead.
2016-01-26 20:57:25 -08:00
Tim Abbott
ea6211c041 Allocate handler ids in AsyncDjangoHandler setup process. 2016-01-26 20:56:53 -08:00
Tim Abbott
ae760a351e Move most of ClientDescriptor.finish_current_handler into library. 2016-01-26 20:56:08 -08:00
Tim Abbott
7df61fccbd Add user_profile_email field to ClientDescriptor. 2016-01-26 20:55:59 -08:00
Tim Abbott
2ea0daab19 Track Tornado handlers by uniquely assigned IDs rather than objects.
This is early preparation for splitting apart Tornado into a queue
server and a frontend server.
2016-01-26 20:55:41 -08:00
Tim Abbott
8b42fdd0d7 Move get_events logic into a backend function in event_queue.py.
This commit is somewhat ugly, but its purpose is to be early
preparation for splitting Tornado into a queue server and a frontend
server, and this code belongs, by and large, in the queue server
component.
2016-01-26 20:51:54 -08:00
Tim Abbott
5a6154c8ba test_helpers: Fetch streams from cache in subscribe_to_stream. 2016-01-26 20:41:01 -08:00
Tim Abbott
a5d4d0aae0 test_backend: Add option to profile the backend test suite. 2016-01-26 20:41:01 -08:00
Tim Abbott
f9791558e9 test_runner: Support continuing running tests after a failure. 2016-01-26 20:41:01 -08:00
Tim Abbott
b43aadad8b test-backend: Rewrite in python to support computing test coverage.
The code for doing test coverage is just a lot cleaner this way over
adding it to the shell script version.

Based on the basic test runner code here:
https://docs.djangoproject.com/en/1.9/topics/testing/advanced/
2016-01-26 20:41:01 -08:00
Tim Abbott
24fd3bbf55 travis: Test whether migrations are consistent with models.
This should automatically catch mistakes where someone updates the
database models but forgets to generate migrations afterwards.
2016-01-26 20:38:46 -08:00
Tim Abbott
5ef57a07e1 Add missing migrations present in models.py.
89a2765553 didn't include the database
migration corresponding to the change, which means it didn't take full
effect when it was merged.

I noticed this because `manage.py makemigrations` would generate these
migrations; that suggests a good idea for a test to add.
2016-01-26 20:38:46 -08:00
Tim Abbott
1c73c992dd Fix missing puppet dependencies on postgres package. 2016-01-26 20:32:33 -08:00
Tim Abbott
2e16b44b24 puppet: Use $postgres_version in postgres template.
This eliminates hardcoding of the postgres version from the Zulip
puppet configuration.
2016-01-26 20:32:33 -08:00
Tim Abbott
806aa986b7 puppet: Use a variable to configure the postgres version. 2016-01-26 20:32:33 -08:00
Tim Abbott
a3ac56efe2 puppet: Make apt repository conditional on the Ubuntu version.
We still will need to address this in the install script as well.
2016-01-26 20:32:33 -08:00
Tim Abbott
f6c59feb05 Document the puppet configuration somewhat in zulip::voyager. 2016-01-26 20:32:33 -08:00
Tim Abbott
345b5254d7 puppet: Move default nginx configuration out of voyager.pp. 2016-01-26 20:32:33 -08:00
Tim Abbott
dd61e3f97d puppet: Move memcached and rabbitmq include out of app_frontend_base.pp. 2016-01-26 20:32:33 -08:00
Tim Abbott
c3153274c1 puppet: Move memcached into its own puppet module. 2016-01-26 20:32:33 -08:00
Tim Abbott
8a0e07fe1a puppet: Rename app_frontend.pp to app_frontend_base.pp.
This will enable us to move the remaining app-frontend related content
out of voyager.pp.
2016-01-26 20:32:33 -08:00
Tim Abbott
91286d00aa puppet: Move Zulip apt repository to its own manifest. 2016-01-26 20:32:33 -08:00
Tim Abbott
69dd17dfb6 puppet: Move prod-static creation from voyager.pp to app_frontend.pp.
Every app frontend will need this directory and this should help
enable more modular puppet rules.
2016-01-26 20:32:33 -08:00
Tim Abbott
702f501638 puppet: Move tuned postgres configuration out of voyager.pp.
This should make it easier for someone to run just the tuned Zulip
database on one server and the Zulip frontend on another.
2016-01-26 20:32:28 -08:00
Tim Abbott
d5f04bd20b Rename zulip::postgres_appdb to zulip::postgres_appdb_base.
The purpose of this rename is to allow us to move the postgres-related
configuration out of voyager.pp.
2016-01-26 20:30:12 -08:00
Tim Abbott
3f27573cb2 puppet: Move several debugging tool dependencies out of base.pp. 2016-01-26 20:30:11 -08:00
Tim Abbott
fdc7f5b86a Vagrantfile: Default to using LXC when Virtualbox is also available.
This solves the problem reported in #331 with needing to specify
--provider=lxc to use the LXC provider in an Ubuntu Linux environment;
additionally, it adds the LXC option needed to run LXC on Ubuntu
15.10, but not on 14.04 where that option is unavailable and would
totally break LXC.
2016-01-23 11:45:47 -08:00
Tim Abbott
50bc32dc95 integrations: Document server-side setup for twitter integration. 2016-01-23 11:44:53 -08:00
Tim Abbott
c6d06b0c4e Add zulip distribution tarballs to backups. 2016-01-23 11:44:15 -08:00
Tim Abbott
529d7a2877 Release API version 0.2.5. 2016-01-23 11:38:47 -08:00
Tim Abbott
d3588cb7d0 api: Include get_subscribers endpoint in public release.
It's possible we should just eliminate this mechanism, but this fixes
a proximal problem where the multi-line get_subscribers endpoint
description was being handled wrong.
2016-01-23 11:38:42 -08:00
Tim Abbott
fdf708039b Disable empty-stream notifications for email gateway bot. 2016-01-23 11:37:52 -08:00
Vladislav Manchev
df4d1b3c14 Add linting code for detecting shebang bashisms.
This will prevent regressions in OpenBSD compatibility, since OpenBSD
doesn't support passing arguments in the #! line.
2016-01-21 22:33:55 -08:00
Vladislav Manchev
dfbea01c8f Add support for running OpenBSD in development environment. 2016-01-21 22:33:55 -08:00
Alexander Trost
84f7a1f1ea Make rabbitmq, redis, and memcached configurable via user settings.py.
Previously these were hardcoded in zproject/settings.py to be accessed
on localhost.

[Modified by Tim Abbott to adjust comments and fix configure-rabbitmq]
2016-01-21 22:07:56 -08:00
Tim Abbott
6943a142ea Fix postgres errors in Travis CI again.
Travis CI's model of installing every version of postgres on the test
VM and then shutting all the versions other than the one requested
down seems to not work very well with doing apt upgrades.  It seems
the best way to resolve this is to just uninstall the versions we
don't need.
2016-01-21 22:07:10 -08:00
Tim Abbott
e1e7ea01ca Add changelog for Zulip 1.3.10 release. 2016-01-21 20:24:53 -08:00
Vladislav Manchev
62d021f399 Change administration UI to utilize tabs. 2016-01-12 22:08:10 -08:00
Reid Barton
ed412281d0 Fix typo in Google OAuth error message. 2016-01-12 09:32:09 -05:00
Tim Abbott
5d54f66047 get_deployment_lock: Flush output when reporting lock delays.
This fixes an issue where the output that the lock wasn't unavailable
showed up all at once after the 5 minute timeout.
2016-01-11 21:36:42 -08:00
Tim Abbott
26e9d55e16 deployments: Refactor locking libraries into zulip_tools.py.
The code in update-deployment and upgrade-zulip for managing the
deployment lock was nearly identical.
2016-01-11 21:36:42 -08:00
Tim Abbott
f871090bb6 upgrade-zulip: Archive release tarballs at /home/zulip/archives.
A common issue when doing a Zulip upgrade is trying to pass
upgrade-zulip a tarball path under /root, which doesn't work because
the Zulip user doesn't have permission to read the tarball.  We
could fix this by just unpacking the tarballs as root, but it seemed
like a nicer approach would be to archive the release tarballs
somewhere readable by the Zulip user (/home/zulip/archives) and unpack
them from there.

Fixes #208.
2016-01-11 21:36:42 -08:00
Tim Abbott
c101bf663d Run upgrade-zulip-stage-2 from an absolute path.
This should make it more obvious in tracebacks that we are running the
script from the version of Zulip we're upgrading to, not the old
version.
2016-01-11 21:36:42 -08:00
Tim Abbott
52d0423591 Document structure of upgrade-zulip-stage-2 more clearly. 2016-01-11 21:36:41 -08:00
Tim Abbott
186f563176 Fix deployment locks being leaked when a deployment fails.
The point of the lock is to prevent two deployments happening at the
same time and racing with each other, not to prevent doing any future
deployments after an error happens (which is what the current
implementation does in practice).

Addresses part of #208.
2016-01-11 21:36:41 -08:00
Tim Abbott
2b0394d807 Add documentation explaining what process_fts_updates does. 2016-01-11 08:56:03 -08:00
Tim Abbott
0162dc4bc0 process_fts_updates: Cleanup and document new settings import logic. 2016-01-11 08:56:03 -08:00
Javier Ros
a6a47aacde Add easy support for using a remote postgres database. 2016-01-11 08:56:03 -08:00
Tim Abbott
e3435b9613 Fix broken link to the cute guinea pig image in tutorial.
This link was broken when we hardened the access model for user file
uploads to not work cross-realm.  The right solution is just to
include the image in the codebase so it's guaranteed to exist.

Fixes #205.
2016-01-09 22:52:35 -08:00
Tim Abbott
6d29dd2884 Fix do_remove_default_stream handling of nonstandard input.
Previously:
* It wouldn't raise an exception if the stream didn't exist
* It didn't correctly handle being passed a stream name
that differed in case from the stream name in the database.
2016-01-09 22:52:35 -08:00
Tim Abbott
8099aa5470 Fix passing notifications_stream to set_default_streams.
Previously, this would throw an IntegrityError, because it had just
been added in the loop.
2016-01-09 22:52:35 -08:00
Tim Abbott
c661bc17fb Fix support for having a unique, open realm.
The previous implementation didn't work because HomepageForm rejected
the email as not having a domain.  Additionally, the logic in
accounts_register didn't work with Google auth because that code path
doesn't pass through accounts_home.  Since whether there's a unique
open realm for the server is effectively a configuration property, we
can fix the bug and make the logic clearer by moving it into the
"figure out the user's realm" function.
2016-01-09 22:52:34 -08:00
Tim Abbott
515249ce0a trac: Cleanup documentation of TRAC_NOTIFY_FIELDS. 2016-01-09 20:21:39 -08:00
Tim Abbott
85a8a742e2 Remove unused json_events_register route.
The browser registers for events via loading the home view, not this
interface, and this functionality is available via the API-format
register route anyway.
2016-01-09 20:01:38 -08:00
Tim Abbott
bddf971554 alert_words: Only fetch and cache non-null alert word sets.
This removes from our cache a moderate amount of totally useless alert
word data corresponding to users who don't have any alert words.

Thanks to @dbiollo for the suggestion!
2016-01-09 20:01:38 -08:00
Tim Abbott
84114ab31f Simplify realm_user_count to do just do a database count() query.
Just doing the database query is more readable, and has about the same
performance as before in the case where active user dicts for the
realm are in cache (and is substantially better in the rare case that
this isn't in the cache).

Thanks to @dbiollo for the perf investigation and suggestion!
2016-01-09 20:01:37 -08:00
Tim Abbott
01f613751a Limit DevAuthBackend user list display to 100 users.
This makes it possible to use DevAuthBackend when doing
performance/scalability testing on Zulip with many thousands of users.

It's unlikely that anyone testing this backend will find it valuable
to have more than 100 login buttons on the same page, and if they do,
they can always just change this limit.

Thanks to @dbiollo for the suggestion!
2016-01-09 20:01:37 -08:00
Tim Abbott
dd4ca2f934 Add case-insensitive index on PreregistrationUser.email.
This fixes a performance issue joining a server with a large number of
users.

Thanks to @dbiollo for the suggestion!
2016-01-09 20:01:37 -08:00
Tim Abbott
2ea0fce47e Add UserProfile indexes on is_active and is_bot.
Since we frequently do filters for only active users, these indexes
may help performance in some cases.
2016-01-09 20:01:37 -08:00
Tim Abbott
ebcb569c96 Add case-insensitive index on UserProfile.email.
This fixes a performance issue looking up UserProfile objects for
realms with a large number of users in the case that a UserProfile
object is not in the cache.

Thanks to @dbiollo for the suggestion!
2016-01-09 20:01:37 -08:00
Tim Abbott
a98b0cf35d travis: Workaround postgres 9.1 conflict issues on trusty.
We ran into a bug with the Travis CI infrastructure where it postgres
9.1 is installed on the system, and so when we'd do an apt upgrade
with a new version of 9.1, the 9.1 daemon would end up getting started
and conflict with the 9.3 daemon we were trying to run.
2016-01-09 16:59:43 -08:00
Tim Abbott
e7353902df upgrade-zulip: Restart process-fts-updates if also a DB host.
Previoulsy, process-fts-updates wouldn't be restarted on a server
upgrade in Voyager configurations.
2016-01-09 16:59:43 -08:00
Tim Abbott
75b5a1b8da upgrade-zulip: Stop zulip-senders too when shutting down services. 2016-01-09 16:59:43 -08:00
Tim Abbott
bed847e029 travis: Cache the phantomjs package downloads between builds.
This should hopefully fix the issue we've been seeing with
bitbucket.org rejecting connections from Travis CI by not needing to
connect to them.
2016-01-09 15:46:29 -08:00
Tim Abbott
408ff14be8 Fix parsing html_unescape.py in py3k test.
This file was using print as a function but didn't import
print_function; as a result futurize threw an error every time it
parsed this file.
2016-01-09 15:43:30 -08:00
Tim Abbott
24ebc10ec8 Travis: Display all errors, not just first one, in py3k testing. 2016-01-09 15:43:26 -08:00
Tim Abbott
1cfde054ff Initialize new_message_count to 0 by default.
860cf68716 introduced calls to
notifications.redraw_title() on narrow activation.  This introduced a
bug when the Zulip desktop app reloads while narrowed --
new_message_count would still be set to undefined when
narrow.activate() is called as the page (re)loads, and thus we'd call
window.bridge.updateCount(undefined), resulting in a traceback.

We fix this by just initializing it to 0, rather than using the old
default value of undefined.
2016-01-09 15:43:20 -08:00
Tim Abbott
24e4a33a0e Document the Zulip security model. 2016-01-08 07:52:25 -08:00
Tim Abbott
7f7bb1caee Add extensive new documentation on backups, monitoring, scalability. 2016-01-08 07:52:25 -08:00
Josh Mandel
9ebd80ddba Add "apple-mobile-web-app-capable" meta to HTML.
This allows full-screen mode when launching from a saved app link
(mobile browser -> save link to home screen).  This works on Android,
too, despite the "apple-" prefix.
2016-01-07 23:08:52 -08:00
Josh Mandel
bdb9535251 Don't require link tags to close when checking templates. 2016-01-07 23:08:49 -08:00
Tim Abbott
7ad7e7a082 Changelog: Update with items since the last release. 2015-12-25 16:50:05 -08:00
Tim Abbott
99975400df Move LDAP password configuration to zulip-secrets.conf. 2015-12-25 16:47:48 -08:00
Tim Abbott
af8d75332c Move email_gateway_password to zulip-secrets.conf. 2015-12-25 16:47:48 -08:00
Tim Abbott
8b1d7d7018 Fix missing step in postfix_localmail installation documentation. 2015-12-25 16:47:48 -08:00
Tim Abbott
f4e87936da Silence 'Starting new HTTP connection' logs from requests library. 2015-12-25 16:23:57 -08:00
Tim Abbott
870734fca2 Configure webpack to only display output in case of errors. 2015-12-25 16:23:57 -08:00
Tim Abbott
9d108989f3 Silence SimpleQueueClient info output by default. 2015-12-25 16:23:57 -08:00
Tim Abbott
c5f08022f9 tools/clean-repo: Don't print out the list of .pyc files deleted.
This was never useful output and was pretty spammy.
2015-12-25 16:23:57 -08:00
Tim Abbott
b879b7ff42 Use logger.debug when logging 200/304 output on static assets. 2015-12-25 16:23:57 -08:00
Tim Abbott
dfaf45b2b6 Wrap Django runserver to prevent spammy logging.
Django's `manage.py runserver` prints a relatively low-information log
line for every request of the form:

[14/Dec/2015 00:43:06]"GET /static/js/message_list.js HTTP/1.0" 200 21969

This is pretty spammy, especially given that we already have our own
middleware printing a more detailed version of the same log lines:

2015-12-14 00:43:06,935 INFO     127.0.0.1       GET     200   0ms /static/js/message_list.js (unauth via ?)

Since runserver doesn't have support controlling whether these log
lines are printed, we wrap it with a small bit of code that silences
the log lines for 200/304 requests (aka the uninteresting ones).
2015-12-25 16:23:57 -08:00
Tim Abbott
be9939b2ad Fix tracebacks if HTTP_USER_AGENT is not specified.
Previously, we handled this correctly in some places (like the
decorators) but not in the website flows (accepting ToS, loading /).
2015-12-25 16:23:11 -08:00
Tim Abbott
39e80b351d Add simple tools for fetching content of a pull request by ID. 2015-12-25 16:22:00 -08:00
Reid Barton
b2a92877ff Don't print echo commands in initialize-database post-success message. 2015-12-25 10:38:44 -08:00
Reid Barton
4c3334908a Document that 'localhost' is necessary in ALLOWED_HOSTS.
It's needed for the tornado server. Otherwise, you get errors like

2015-12-20 09:33:55,124 ERROR    Internal Server Error: /api/v1/events
Traceback (most recent call last):
  File "/home/zulip/deployments/2015-12-20-13-44-47/zerver/management/commands/runtornado.py", line 209, in get_response
    response = middleware_method(request)
  File "/usr/lib/python2.7/dist-packages/django/middleware/common.py", line 62, in process_request
    host = request.get_host()
  File "/usr/lib/python2.7/dist-packages/django/http/request.py", line 101, in get_host
    raise DisallowedHost(msg)
DisallowedHost: Invalid HTTP_HOST header: 'localhost:9993'. You may need to add u'localhost' to ALLOWED_HOSTS.
2015-12-25 10:07:12 -08:00
Reid Barton
64a142f0a2 Fix running postgres-init-db via a relative path.
If the user runs ./scripts/setup/postgres-init-db, then dirname "$0"
would no longer refer to the correct directory after cd /.
2015-12-25 10:06:45 -08:00
Kara McNair
fd66d9f703 Show 5 most recent "Private messages" when clicked.
Like the Stream Subject lists, Private messages are now shown
when the user clicks on the "Private message" link. User can drill in
to get more than 5 conversations. Selecting PMs from the user or group
PM lists on the right sidebar also opens the list & highlights the
selected conversation.

[Edited by tabbott@mit.edu to fix some small bugs.]
2015-12-15 07:52:54 -08:00
Tim Abbott
29fa601328 Rename active-subject-filter to active-sub-filter.
This makes the name more generic so we can reuse it for private
message filters as well.
2015-12-15 07:52:54 -08:00
Tim Abbott
87acb2be09 stream_list: Factor out reset_to_unnarrowed helper. 2015-12-15 07:52:54 -08:00
Tim Abbott
c404f3189c stream_list: move remove_expanded_subjects earlier in file. 2015-12-15 07:52:54 -08:00
Tim Abbott
7bb11fe09a topic zoom: Clean up unnecessary jquery selectors. 2015-12-15 07:52:54 -08:00
Liz Sander
860cf68716 Show current narrow in page title.
Fixes issue #157.
2015-12-14 21:21:30 -08:00
Javier Ros
ab89ef501f Add support for a development environment with Docker. 2015-12-14 18:22:56 -08:00
Tim Abbott
e95739961f Remove now obsolete /json/send_message route. 2015-12-12 18:14:08 -08:00
Tim Abbott
9cec758854 Remove now obsolete /json/subscriptions/add endpoint. 2015-12-12 18:14:08 -08:00
Allie Jones
1bd1291f3c Replace json/send_message endpoint usage with json/message. 2015-12-12 18:14:08 -08:00
Allie Jones
29a4b51e52 Replace /json/subscriptions/add usage with /json/users/me/subscription. 2015-12-12 18:14:08 -08:00
Tim Abbott
023f45190f rest_dispatch: Add support for using with websockets integration. 2015-12-12 18:14:08 -08:00
Kara McNair
c947f3ed3c Add Zulip-specific details in jslint used_before_a error message.
Previously, it wasn't clear why you were getting an error when trying
to reference a newly added global variable.
2015-12-07 20:33:36 -08:00
Tim Abbott
2be7ac8d70 travis: Fix prompting for user input in production-helper. 2015-12-07 20:33:36 -08:00
Tim Abbott
0fe819eb57 lint: Tighten lint rules around whitespace around '%' comprehensions. 2015-12-05 15:29:42 -08:00
Tim Abbott
69b6b60017 lint: Clean up whitespace_rules.
Several of these rules only apply to one of Python and Javascript, and
this simplifies the logic and should make our linter code more readable.

In the process, we add support for per-rule/file pair exclusions to
handle the tab exception for codehilite.py.
2015-12-05 15:29:42 -08:00
Tim Abbott
a712954c59 lint: Rewrite custom checks to use a more consistent framework. 2015-12-05 15:29:42 -08:00
Tim Abbott
8a18e78a65 Add lint rules checking for our % comprehension style. 2015-12-05 15:29:42 -08:00
Tim Abbott
a79e89b28f Cleanup remaining usage of % comprehensions without explicit tuples. 2015-12-05 15:29:42 -08:00
Tim Abbott
74853709a8 Rename NOTIFICATION_STREAM_NAME to clarify it's a default. 2015-12-05 14:14:44 -08:00
Tim Abbott
6b1494927d set_default_streams: Don't force-create notifications stream. 2015-12-05 14:14:44 -08:00
Tim Abbott
0ce14bec44 test_set_default_streams: Use a newly created test realm.
The test "zulip.com" realm seems to not have notifications_stream set.
2015-12-05 14:11:45 -08:00
Josh Mandel
716e2d9184 Add integration for Travis CI. 2015-11-30 20:41:33 -08:00
Tim Abbott
05acd510c0 Make reload save options required explicit arguments. 2015-11-30 08:49:39 -08:00
Tim Abbott
12bff0441c Save compose state when reloading due to 'declare bankruptcy'. 2015-11-30 08:49:37 -08:00
Dylan Dumesnil
2f4037ae2f Save compose state when reloading due to /json/get_events error.
Fixes #203.
2015-11-30 08:47:36 -08:00
Dylan Dumesnil
24b63f30ba Refactor reload.js to allow saving only some state when reloading.
The reload state is now divided into 3 different components:
  pointer, narrow, compose
2015-11-30 08:47:36 -08:00
Jason Michalski
9a3331acaf Run provision.py outside of the virtualenv
By default we are placed inside a virtualenv by the .bash_profile using
/usr/bin/python forces the provisioning script to run outside of this
virtualenv.
2015-11-25 19:29:13 -08:00
Tim Abbott
cd0a8e7e5a Marshall tutorial route inputs as JSON strings.
These routes previously didn't follow our standard convention of
sending arguments in JSON format, and so broke when we started
checking the argument format in
123d51e3aa.

Fixes #333.
2015-11-24 22:46:25 -08:00
Chris Chapman
44a9e1dff5 Fix for setting file upload size through settings file.
(Slightly tweaked by Tim Abbott to change the variable name, docs, and
default values).

Fixes #276.
2015-11-24 06:06:46 -08:00
Tim Abbott
07419104a5 nginx: Enable gzip compression on main content types.
Apparently, previously nginx was only compressing text/html content.
This should result in a substantial savings in network traffic -- some
quick testing I did found it cut the total data transferred for
loading a logged-in zulip.com instance from 3MB to 1.2MB.
2015-11-24 05:21:49 -08:00
Tim Abbott
123d51e3aa Add string validators to tutorial routes. 2015-11-24 05:20:37 -08:00
Tim Abbott
aa33a0daec Move users views into their own file. 2015-11-24 05:20:37 -08:00
Tim Abbott
4d79083cf5 Move tutorial views into their own file. 2015-11-24 05:20:37 -08:00
Tim Abbott
f77b0bdb43 Move alert_words views into their own file. 2015-11-24 05:20:37 -08:00
Tim Abbott
e64a3d0fae Move reporting views to their own file. 2015-11-24 05:20:37 -08:00
Tim Abbott
8526d02370 Move settings views into their own file. 2015-11-24 05:20:37 -08:00
Tim Abbott
37d4a11610 Move streams views into their own file. 2015-11-24 05:20:37 -08:00
Tim Abbott
6a8318ddcd Add istanbul for running node coverage reports.
`tools/test-js-with-node cover` needs istanbul to be installed in
order to work; we might as well install it by default rather than
having it be an extra step users need to deal with.

Of course, since this is only needed in the development environment,
this could suggest we want to fork/conditionalize package.json, but I
think for now it's reasonable to just install everything we use
somewhere -- the npm list is still pretty short and we have that issue
anyway with webpack-dev-server.
2015-11-24 05:20:15 -08:00
Josh Mandel
15dae10383 Only show "Sign up with google" when enabled 2015-11-24 06:13:09 +00:00
Luke Faraone
db5c460cfc Run django.setup() in nagios plugins to avoid exceptions on 1.8
If running on Django 1.8, running these plugins would die with the below. A fix
for this is to run `django.setup()` before interacting with Django.

Refs:
    https://docs.djangoproject.com/en/1.8/ref/applications/#troubleshooting

```

Traceback (most recent call last):
  File "/usr/lib/nagios/plugins/check_send_receive_time", line 103, in <module>
    sender = get_user_profile_by_email(settings.NAGIOS_SEND_BOT)
  File "/home/zulip/deployments/current/zerver/lib/cache.py", line 113, in func_with_caching
    val = func(*args, **kwargs)
  File "/home/zulip/deployments/current/zerver/models.py", line 1073, in get_user_profile_by_email
    return UserProfile.objects.select_related().get(email__iexact=email.strip())
  File "/usr/lib/python2.7/dist-packages/django/db/models/query.py", line 328, in get
    num = len(clone)
  File "/usr/lib/python2.7/dist-packages/django/db/models/query.py", line 144, in __len__
    self._fetch_all()
  File "/usr/lib/python2.7/dist-packages/django/db/models/query.py", line 977, in _fetch_all
    self._result_cache = list(self.iterator())
  File "/usr/lib/python2.7/dist-packages/django/db/models/query.py", line 238, in iterator
    results = compiler.execute_sql()
  File "/usr/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 829, in execute_sql
    sql, params = self.as_sql()
  File "/usr/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 378, in as_sql
    extra_select, order_by, group_by = self.pre_sql_setup()
  File "/usr/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 48, in pre_sql_setup
    self.setup_query()
  File "/usr/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 39, in setup_query
    self.select, self.klass_info, self.annotation_col_map = self.get_select()
  File "/usr/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 206, in get_select
    related_klass_infos = self.get_related_selections(select)
  File "/usr/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 700, in get_related_selections
    [f.name], opts, root_alias)
  File "/usr/lib/python2.7/dist-packages/django/db/models/sql/query.py", line 1471, in setup_joins
    names, opts, allow_many, fail_on_missing=True)
  File "/usr/lib/python2.7/dist-packages/django/db/models/sql/query.py", line 1372, in names_to_path
    if field.is_relation and not field.related_model:
  File "/usr/lib/python2.7/dist-packages/django/utils/functional.py", line 60, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/usr/lib/python2.7/dist-packages/django/db/models/fields/related.py", line 110, in related_model
    apps.check_models_ready()
  File "/usr/lib/python2.7/dist-packages/django/apps/registry.py", line 131, in check_models_ready
    raise AppRegistryNotReady("Models aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.
```
2015-11-23 18:54:08 +00:00
Allie Jones
11c69fb0b2 Add documentation on the front end build process and webpack. 2015-11-20 20:32:01 -08:00
Allie Jones
4f9ef4ca29 Update list of by-hand installation apt-get packages to match the list in provision.py 2015-11-20 10:22:15 -08:00
Allie Jones
8f24beec21 Update by-hand installation instructions to include node/npm 2015-11-20 10:22:13 -08:00
Tim Abbott
1a01e65be2 Expand and reorganize .gitignore content.
Primarily this makes sure all the log files we generate are ignored.

A good follow-up project to this would be to move all the log files to
a fixed directory so that they're not creating a mess in the main
filespace.
2015-11-19 09:12:08 -08:00
Tim Abbott
4474a11a3a Administration: Move deactivated users section to more sensible place. 2015-11-16 21:10:06 -08:00
Tim Abbott
58aba59e36 Apply the nice settings page CSS to the Administration page. 2015-11-16 21:10:06 -08:00
Tim Abbott
be112d2c9d SlowQueryWorker: Don't reference potentially unset STATSD_PREFIX. 2015-11-16 21:10:06 -08:00
Tim Abbott
5fa6260ae8 Add changelog for Zulip 1.3.9 release. 2015-11-16 08:46:48 -08:00
Tim Abbott
7395003e6a Fix buggy #! lines using "/usr/bin/env python2.7 -u".
The #! line processing interpreted the argument to pass to `env` as
"python2.7 -u", which obviously isn't a real program.

We fix this by setting the PYTHONUNBUFFERED environment variable
inside the program, which has the same effect.

Thanks to Dan Fedele for the bug report and suggested solution!
2015-11-16 08:46:48 -08:00
537 changed files with 15302 additions and 5578 deletions

2
.gitattributes vendored
View File

@@ -14,7 +14,7 @@
/zproject/local_settings.py export-ignore
/zproject/test_settings.py export-ignore
/zerver/fixtures export-ignore
/zerver/tests.py export-ignore
/zerver/tests export-ignore
/frontend_tests export-ignore
/node_modules export-ignore
/humbug export-ignore

21
.gitignore vendored
View File

@@ -2,8 +2,18 @@
*~
/all_messages_log.*
/event_log/*
/server.log
/digest.log*
/errors.log*
/manage.log*
/server.log*
/workers.log*
/email-deliverer.log
/email-mirror.log
/sync_ldap_user_data.log
/update-prod-static.log
frontend_tests/casper_tests/server.log
frontend_tests/casper_lib/test_credentials.js
memcached_prefix
/prod-static
/errors/*
*.sw[po]
@@ -15,14 +25,10 @@ zerver/fixtures/migration-status
zerver/fixtures/test_data1.json
.kdev4
zulip.kdev4
memcached_prefix
remote_cache_prefix
coverage/
/queue_error
/workers.log
.test-js-with-node.html
digest.log
errors.log
manage.log
.kateproject.d/
.kateproject
*.kate-swp
@@ -34,4 +40,7 @@ static/third/gemoji/
static/third/zxcvbn/
tools/emoji_dump/bitmaps/
tools/emoji_dump/*.ttx
tools/phantomjs
node_modules
uploads/
test_uploads/

View File

@@ -1,9 +1,11 @@
before_install:
- nvm install 0.10
- nvm install 0.10
install:
- tools/travis/setup-$TEST_SUITE
cache:
- apt: false
- directories:
- /srv/phantomjs
env:
- TEST_SUITE=frontend
- TEST_SUITE=backend
@@ -12,6 +14,10 @@ env:
language: python
python:
- "2.7"
matrix:
include:
- python: "3.4"
env: TEST_SUITE=mypy
# command to run tests
script:
- ./tools/travis/$TEST_SUITE

12
Dockerfile Normal file
View File

@@ -0,0 +1,12 @@
FROM ubuntu:trusty
EXPOSE 9991
RUN apt-get update && apt-get install -y \
python-pbs \
wget
RUN useradd -d /home/zulip -m zulip && echo 'zulip ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
USER zulip
WORKDIR /srv/zulip

View File

@@ -25,23 +25,23 @@ such as Mac via Virtualbox (but everything will be 2-3x slower).
sudo apt-get install vagrant lxc lxc-templates cgroup-lite redir
vagrant plugin install vagrant-lxc
```
You may want to [configure sudo to be passwordless when using Vagrant LXC][avoiding-sudo].
* If your host is Ubuntu 14.04, you will need to [download a newer
version of Vagrant](https://www.vagrantup.com/downloads.html), and
then do the following:
version of Vagrant][vagrant-dl], and then do the following:
```
sudo apt-get install lxc lxc-templates cgroup-lite redir
sudo dpkg -i vagrant*.deb # in directory where you downloaded vagrant
vagrant plugin install vagrant-lxc
```
You may want to [configure sudo to be passwordless when using Vagrant LXC][avoiding-sudo].
* For other Linux hosts with a kernel above 3.12, [follow the Vagrant
LXC installation
instructions](https://github.com/fgrehm/vagrant-lxc) to get Vagrant
with LXC for your platform.
LXC installation instructions][vagrant-lxc] to get Vagrant with LXC
for your platform.
* If your host is OS X or older Linux, [download VirtualBox](https://www.virtualbox.org/wiki/Downloads),
[download Vagrant](https://www.vagrantup.com/downloads.html), and install them both.
* If your host is OS X or older Linux, [download VirtualBox][vbox-dl],
[download Vagrant][vagrant-dl], and install them both.
* If you're on OS X and have VMWare, it should be possible to patch
Vagrantfile to use the VMWare vagrant provider which should perform
@@ -54,6 +54,11 @@ such as Mac via Virtualbox (but everything will be 2-3x slower).
core.autocrlf=false` to avoid Windows line endings being added to
files (this causes weird errors).
[vagrant-dl]: https://www.vagrantup.com/downloads.html
[vagrant-lxc]: https://github.com/fgrehm/vagrant-lxc
[vbox-dl]: https://www.virtualbox.org/wiki/Downloads
[avoiding-sudo]: https://github.com/fgrehm/vagrant-lxc#avoiding-sudo-passwords
Once that's done, simply change to your zulip directory and run
`vagrant up` in your terminal to install the development server. This
will take a long time on the first run because Vagrant needs to
@@ -74,13 +79,40 @@ source /srv/zulip-venv/bin/activate
To get shell access to the virtual machine running the server to run
lint, management commands, etc., use `vagrant ssh`.
(A small note on tools/run-dev.py: the `--interface=''` option will make
the development server listen on all network interfaces. While this
is correct for the Vagrant guest sitting behind a NAT, you probably
don't want to use that option when using run-dev.py in other environments).
(A small note on tools/run-dev.py: the `--interface=''` option will
make the development server listen on all network interfaces. While
this is correct for the Vagrant guest sitting behind a NAT, you
probably don't want to use that option when using run-dev.py in other
environments).
At this point you should [read about using the development environment](https://github.com/zulip/zulip/blob/master/README.dev.md#using-the-development-environment).
At this point you should [read about using the development
environment][using-dev].
[using-dev]: #using-the-development-environment
## Specifying a proxy
If you need to use a proxy server to access the Internet, you will
need to specify the proxy settings before running `Vagrant up`.
First, install the Vagrant plugin `vagrant-proxyconf`:
```
vagrant plugin install vagrant-proxyconf.
```
Then create `~/.zulip-vagrant-config` and add the following lines to
it (with the appropriate values in it for your proxy):
```
HTTP_PROXY http://proxy_host:port
HTTPS_PROXY http://proxy_host:port
NO_PROXY localhost,127.0.0.1,.example.com
```
Now run `vagrant up` in your terminal to install the development
server. If you ran `vagrant up` before and failed, you'll need to run
`vagrant destroy` first to clean up the failed installation.
Using provision.py without Vagrant
----------------------------------
@@ -91,7 +123,6 @@ running:
```
sudo apt-get update
sudo apt-get install -y python-pbs
python /srv/zulip/provision.py
cd /srv/zulip
@@ -110,19 +141,24 @@ instructions should work.
Install the following non-Python dependencies:
* libffi-dev — needed for some Python extensions
* postgresql 9.1 or later — our database (also install development headers)
* postgresql 9.1 or later — our database (client, server, headers)
* nodejs 0.10 (and npm)
* memcached (and headers)
* rabbitmq-server
* libldap2-dev
* python-dev
* redis-server — rate limiting
* tsearch-extras — better text search
* libfreetype6-dev - needed before you pip install Pillow to properly generate emoji PNGs
* libfreetype6-dev needed before you pip install Pillow to properly generate emoji PNGs
### On Debian or Ubuntu systems:
```
sudo apt-get install libffi-dev memcached rabbitmq-server libldap2-dev python-dev redis-server postgresql-server-dev-all libmemcached-dev libfreetype6-dev
sudo apt-get install closure-compiler libfreetype6-dev libffi-dev \
memcached rabbitmq-server libldap2-dev redis-server \
postgresql-server-dev-all libmemcached-dev python-dev \
hunspell-en-us nodejs nodejs-legacy npm git yui-compressor \
puppet gettext
# If on 12.04 or wheezy:
sudo apt-get install postgresql-9.1
@@ -144,21 +180,26 @@ Now continue with the "All systems" instructions below.
### On Fedora 22 (experimental):
These instructions are experimental and may have bugs; patches welcome!
These instructions are experimental and may have bugs; patches
welcome!
```
sudo dnf install libffi-devel memcached rabbitmq-server openldap-devel python-devel redis postgresql-server postgresql-devel postgresql libmemcached-devel freetype-devel
sudo dnf install libffi-devel memcached rabbitmq-server \
openldap-devel python-devel redis postgresql-server \
postgresql-devel postgresql libmemcached-devel freetype-devel \
nodejs npm yuicompressor closure-compiler gettext
```
Now continue with the Common to Fedora/CentOS instructions below.
### On CentOS 7 Core (experimental):
These instructions are experimental and may have bugs; patches welcome!
These instructions are experimental and may have bugs; patches
welcome!
```
# Add user zulip to the system (not necessary if you configured zulip as the administrator
# user during the install process of CentOS 7).
# Add user zulip to the system (not necessary if you configured zulip
# as the administrator user during the install process of CentOS 7).
useradd zulip
# Create a password for zulip user
@@ -172,13 +213,16 @@ zulip ALL=(ALL) ALL
# Switch to zulip user
su zulip
# Enable EPEL 7 repo so we can install rabbitmq-server, redis and other dependencies
# Enable EPEL 7 repo so we can install rabbitmq-server, redis and
# other dependencies
sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
# Install dependencies
sudo yum install libffi-devel memcached rabbitmq-server openldap-devel python-devel redis postgresql-server \
postgresql-devel postgresql libmemcached-devel wget python-pip openssl-devel freetype-devel libjpeg-turbo-devel \
zlib-devel nodejs
sudo yum install libffi-devel memcached rabbitmq-server openldap-devel
python-devel redis postgresql-server postgresql-devel postgresql \
libmemcached-devel wget python-pip openssl-devel freetype-devel \
libjpeg-turbo-devel zlib-devel nodejs yuicompressor \
closure-compiler gettext
# We need these packages to compile tsearch-extras
sudo yum groupinstall "Development Tools"
@@ -203,6 +247,40 @@ host all all ::1/128 md5
Now continue with the Common to Fedora/CentOS instructions below.
### On OpenBSD 5.8 (experimental):
These instructions are experimental and may have bugs; patches
welcome!
```
doas pkg_add sudo bash gcc postgresql-server redis rabbitmq \
memcached node libmemcached py-Pillow py-cryptography py-cffi
# Get tsearch_extras and build it (using a modified version which
# aliases int4 on OpenBSD):
git clone https://github.com/blablacio/tsearch_extras
cd tsearch_extras
gmake && sudo gmake install
# Point environment to custom include locations and use newer GCC
# (needed for Node modules):
export CFLAGS="-I/usr/local/include -I/usr/local/include/sasl"
export CXX=eg++
# Create tsearch_data directory:
sudo mkdir /usr/local/share/postgresql/tsearch_data
# Hack around missing dictionary files -- need to fix this to get the
# proper dictionaries from what in debian is the hunspell-en-us
# package.
sudo touch /usr/local/share/postgresql/tsearch_data/english.stop
sudo touch /usr/local/share/postgresql/tsearch_data/en_us.dict
sudo touch /usr/local/share/postgresql/tsearch_data/en_us.affix
```
Now continue with the All Systems instructions below.
### Common to Fedora/CentOS instructions
```
@@ -213,8 +291,9 @@ cd ts2
make
sudo make install
# Hack around missing dictionary files -- need to fix this to get
# the proper dictionaries from what in debian is the hunspell-en-us package.
# Hack around missing dictionary files -- need to fix this to get the
# proper dictionaries from what in debian is the hunspell-en-us
# package.
sudo touch /usr/share/pgsql/tsearch_data/english.stop
sudo touch /usr/share/pgsql/tsearch_data/en_us.dict
sudo touch /usr/share/pgsql/tsearch_data/en_us.affix
@@ -222,7 +301,8 @@ sudo touch /usr/share/pgsql/tsearch_data/en_us.affix
# Edit the postgres settings:
sudo vi /var/lib/pgsql/data/pg_hba.conf
# Add this line before the first uncommented line to enable password auth:
# Add this line before the first uncommented line to enable password
# auth:
host all all 127.0.0.1/32 md5
# Start the services
@@ -237,18 +317,25 @@ Finally continue with the All Systems instructions below.
### All Systems:
```
pip install -r requirements.txt
pip install --no-deps -r requirements.txt
./tools/install-phantomjs
./tools/install-mypy
./tools/download-zxcvbn
./tools/emoji_dump/build_emoji
./scripts/setup/generate_secrets.py -d
sudo cp ./puppet/zulip/files/postgresql/zulip_english.stop /usr/share/postgresql/9.3/tsearch_data/
if [ $(uname) = "OpenBSD" ]; then sudo cp ./puppet/zulip/files/postgresql/zulip_english.stop /var/postgresql/tsearch_data/; else sudo cp ./puppet/zulip/files/postgresql/zulip_english.stop /usr/share/postgresql/9.3/tsearch_data/; fi
./scripts/setup/configure-rabbitmq
./tools/postgres-init-dev-db
./tools/do-destroy-rebuild-database
./tools/postgres-init-test-db
./tools/do-destroy-rebuild-test-database
npm install
```
If `npm install` fails, the issue may be that you need a newer version
of `npm`. You can use `npm install -g npm` to update your version of
`npm` and try again.
To start the development server:
```
@@ -257,6 +344,94 @@ To start the development server:
… and visit [http://localhost:9991/](http://localhost:9991/).
#### Proxy setup for by-hand installation
If you are building the development environment on a network where a
proxy is required to access the Internet, you will need to set the
proxy in the environment as follows:
- On Ubuntu, set the proxy environment variables using:
```
export https_proxy=http://proxy_host:port
export http_proxy=http://proxy_host:port
```
- And set the npm proxy and https-proxy using:
```
npm config set proxy http://proxy_host:port
npm config set https-proxy http://proxy_host:port
```
Using Docker (experimental)
---------------------------
The docker instructions for development are experimental, so they may
have bugs. If you try them and run into any issues, please report
them!
You can also use Docker to run a Zulip development environment.
First, you need to install Docker in your development machine
following the [instructions][docker-install]. Some other interesting
links for somebody new in Docker are:
* [Get Started](https://docs.docker.com/linux/started/)
* [Understand the architecture](https://docs.docker.com/engine/introduction/understanding-docker/)
* [Docker run reference](https://docs.docker.com/engine/reference/run/)
* [Dockerfile reference](https://docs.docker.com/engine/reference/builder/)
[docker-install]: https://docs.docker.com/engine/installation/
Then you should create the Docker image based on Ubuntu Linux, first
go to the directory with the Zulip source code:
```
docker build -t user/zulipdev .
```
Now you're going to install Zulip dependencies in the image:
```
docker run -itv $(pwd):/srv/zulip -p 80:9991 user/zulipdev /bin/bash
$ /usr/bin/python /srv/zulip/provision.py --docker
docker ps -af ancestor=user/zulipdev
docker commit -m "Zulip installed" <container id> user/zulipdev:v2
```
Finally you can run the docker server with:
```
docker run -itv $(pwd):/srv/zulip -p 80:9991 user/zulipdev:v2 \
/srv/zulip/scripts/start-dockers
```
If you want to connect to the Docker instance to build a release
tarball you can use:
```
docker ps
docker exec -it <container id> /bin/bash
$ source /home/zulip/.bash_profile
$ <Your commands>
$ exit
```
To stop the server use:
```
docker ps
docker kill <container id>
```
If you want to run all the tests you need to start the servers first,
you can do it with:
```
docker run -itv $(pwd):/srv/zulip user/zulipdev:v2 /bin/bash
$ scripts/test-all-docker
```
You can modify the source code in your development machine and review
the results in your browser.
Using the Development Environment
=================================
@@ -267,7 +442,7 @@ server homepage just shows a list of the users that exist on the
server and you can login as any of them by just clicking on a user.
This setup saves time for the common case where you want to test
something other than the login process; to test the login process
you'll want to change AUTHENTICATION_BACKENDS in the not-PRODUCTION
you'll want to change `AUTHENTICATION_BACKENDS` in the not-PRODUCTION
case of `zproject/settings.py` from zproject.backends.DevAuthBackend
to use the auth method(s) you'd like to test.
@@ -283,10 +458,10 @@ browser window to see changes take effect.
* If you change Python code used by the the main Django/Tornado server
processes, these services are run on top of Django's [manage.py
runserver](https://docs.djangoproject.com/en/1.8/ref/django-admin/#runserver-port-or-address-port),
which will automatically restart the Zulip Django and Tornado servers
whenever you save changes to Python code. You can watch this happen
in the `run-dev.py` console to make sure the backend has reloaded.
runserver][django-runserver] which will automatically restart the
Zulip Django and Tornado servers whenever you save changes to Python
code. You can watch this happen in the `run-dev.py` console to make
sure the backend has reloaded.
* The Python queue workers don't automatically restart when you save
changes (or when they stop running), so you will want to ctrl-C and
@@ -295,28 +470,34 @@ queue workers or if a queue worker has crashed.
* If you change the database schema, you'll need to use the standard
Django migrations process to create and then run your migrations; see
the [new feature
tutorial](http://zulip.readthedocs.org/en/latest/new-feature-tutorial.html)
for an example. Additionally you should check out the [detailed
testing docs](http://zulip.readthedocs.org/en/latest/testing.html) for
how to run the tests properly after doing a migration.
the [new feature tutorial][new-feature-tutorial] for an example.
Additionally you should check out the [detailed testing
docs][testing-docs] for how to run the tests properly after doing a
migration.
(In production, everything runs under supervisord and thus will
restart if it crashes, and `upgrade-zulip` will take care of running
migrations and then cleanly restaring the server for you).
[django-runserver]: https://docs.djangoproject.com/en/1.8/ref/django-admin/#runserver-port-or-address-port
[new-feature-tutorial]: http://zulip.readthedocs.io/en/latest/new-feature-tutorial.html
[testing-docs]: http://zulip.readthedocs.io/en/latest/testing.html
Running the test suite
======================
For more details, check out the [detailed testing
docs](http://zulip.readthedocs.org/en/latest/testing.html).
For more details, especially on how to write tests, check out the
[detailed testing docs][tdocs].
[tdocs]: http://zulip.readthedocs.io/en/latest/testing.html
To run all the tests, do this:
```
./tools/test-all
```
For the Vagrant environment, you'll want to first enter the environment:
For the Vagrant environment, you'll want to first enter the
environment:
```
vagrant ssh
source /srv/zulip-venv/bin/activate
@@ -330,7 +511,7 @@ time debugging a test failure, e.g.:
```
./tools/lint-all # Runs all the linters in parallel
./tools/test-backend zerver.test_bugdown.BugdownTest.test_inline_youtube
./tools/test-backend zerver.tests.test_bugdown.BugdownTest.test_inline_youtube
./tools/test-js-with-casper 10-navigation.js
./tools/test-js-with-node # Runs all node tests but is very fast
```
@@ -359,5 +540,15 @@ Possible testing issues
above. Afterwards, re-run the `init*-db` and the
`do-destroy-rebuild*-database` scripts.
- When building the development environment using Vagrant and the LXC provider, if you encounter permissions errors, you may need to `chown -R 1000:$(whoami) /path/to/zulip` on the host before running `vagrant up` in order to ensure that the synced directory has the correct owner during provision. This issue will arise if you run `id username` on the host where `username` is the user running Vagrant and the output is anything but 1000.
This seems to be caused by Vagrant behavior; more information can be found here https://github.com/fgrehm/vagrant-lxc/wiki/FAQ#help-my-shared-folders-have-the-wrong-owner
- When building the development environment using Vagrant and the LXC
provider, if you encounter permissions errors, you may need to
`chown -R 1000:$(whoami) /path/to/zulip` on the host before running
`vagrant up` in order to ensure that the synced directory has the
correct owner during provision. This issue will arise if you run `id
username` on the host where `username` is the user running Vagrant
and the output is anything but 1000.
This seems to be caused by Vagrant behavior; for more information,
see [the vagrant-lxc FAQ entry about shared folder permissions
][lxc-sf].
[lxc-sf]: https://github.com/fgrehm/vagrant-lxc/wiki/FAQ#help-my-shared-folders-have-the-wrong-owner)

109
README.md
View File

@@ -12,6 +12,11 @@ missed-message emails, desktop apps, and much more.
Further information on the Zulip project and its features can be found
at https://www.zulip.org.
[![Build Status][1]][2]
[1]: https://travis-ci.org/zulip/zulip.svg?branch=master
[2]: https://travis-ci.org/zulip/zulip
Installing the Zulip Development environment
============================================
@@ -33,64 +38,92 @@ Contributing to Zulip
Zulip welcomes all forms of contributions! The page documents the
Zulip development process.
* **Pull requests**. Before a pull request can be merged, you need to to sign the [Dropbox
Contributor License Agreement](https://opensource.dropbox.com/cla/).
Also, please skim our [commit message style
guidelines](http://zulip.readthedocs.org/en/latest/code-style.html#commit-messages).
* **Pull requests**. Before a pull request can be merged, you need to
to sign the [Dropbox Contributor License Agreement][cla]. Also,
please skim our [commit message style guidelines][doc-commit-style].
* **Testing**. The Zulip automated tests all run automatically when
you submit a pull request, but you can also run them all in your
development environment following the instructions in the [testing
section](https://github.com/zulip/zulip#running-the-test-suite) below.
docs][doc-test].
* **Developer Documentation**. Zulip has a growing collection of
developer documentation on [Read The Docs](https://zulip.readthedocs.org/).
Recommended reading for new contributors includes the
[directory structure](http://zulip.readthedocs.org/en/latest/directory-structure.html) and
[new feature tutorial](http://zulip.readthedocs.org/en/latest/new-feature-tutorial.html).
developer documentation on [Read The Docs][doc]. Recommended reading
for new contributors includes the [directory structure][doc-dirstruct]
and [new feature tutorial][doc-newfeat].
* **Mailing list and bug tracker** Zulip has a [development discussion
mailing list](https://groups.google.com/forum/#!forum/zulip-devel) and
uses [GitHub issues](https://github.com/zulip/zulip/issues). Feel
free to send any questions or suggestions of areas where you'd love to
see more documentation to the list! Please report any security issues
you discover to support@zulip.com.
* **Mailing lists and bug tracker**. Zulip has a [development
discussion mailing list][gg-devel] and uses [GitHub issues
][gh-issues]. There are also lists for the [Android][email-android]
and [iOS][email-ios] apps. Feel free to send any questions or
suggestions of areas where you'd love to see more documentation to the
relevant list! Please report any security issues you discover to
zulip-security@googlegroups.com.
* **App codebases** This repository is for the Zulip server and web app; the
[desktop](https://github.com/zulip/zulip-desktop),
[Android](https://github.com/zulip/zulip-android), and
[iOS](https://github.com/zulip/zulip-ios) apps are separate
* **App codebases**. This repository is for the Zulip server and web
app; the [desktop][], [Android][], and [iOS][] apps are separate
repositories.
* **Translations**. Zulip is in the process of being translated into
10+ languages, and we love contributions to our translations. See our
[translating documentation](transifex) if you're interested in
contributing!
[cla]: https://opensource.dropbox.com/cla/
[doc]: https://zulip.readthedocs.io/
[doc-commit-style]: http://zulip.readthedocs.io/en/latest/code-style.html#commit-messages
[doc-dirstruct]: http://zulip.readthedocs.io/en/latest/directory-structure.html
[doc-newfeat]: http://zulip.readthedocs.io/en/latest/new-feature-tutorial.html
[doc-test]: https://github.com/zulip/zulip/blob/master/README.dev.md#running-the-test-suite
[gg-devel]: https://groups.google.com/forum/#!forum/zulip-devel
[gh-issues]: https://github.com/zulip/zulip/issues
[desktop]: https://github.com/zulip/zulip-desktop
[android]: https://github.com/zulip/zulip-android
[ios]: https://github.com/zulip/zulip-ios
[email-android]: https://groups.google.com/forum/#!forum/zulip-android
[email-ios]: https://groups.google.com/forum/#!forum/zulip-ios
[transifex]: https://www.transifex.com/zulip/zulip/
How to get involved with contributing to Zulip
==============================================
First, subscribe to the Zulip [development discussion mailing list](https://groups.google.com/forum/#!forum/zulip-devel).
First, subscribe to the Zulip [development discussion mailing
list][gg-devel].
The Zulip project uses a system of labels in our [issue
tracker](https://github.com/zulip/zulip/issues) to make it easy to
find a project if you don't have your own project idea in mind or want
to get some experience with working on Zulip before embarking on a
larger project you have in mind:
tracker][gh-issues] to make it easy to find a project if you don't
have your own project idea in mind or want to get some experience with
working on Zulip before embarking on a larger project you have in
mind:
* [Bite Size](https://github.com/zulip/zulip/labels/bite%20size):
Smaller projects that could be a great first contribution.
* [Integrations](https://github.com/zulip/zulip/labels/integrations).
Integrate Zulip with another piece of software and contribute it
back to the community! Writing an integration can be a great
started project. There's some brief documentation on the best way
to write integrations at https://github.com/zulip/zulip/issues/70.
* [Documentation](https://github.com/zulip/zulip/labels/documentation).
back to the community! Writing an integration can be a great first
contribution. There's detailed documentation on how to write
integrations in [the Zulip integration writing
guide](https://zulip.readthedocs.io/en/latest/integration-guide.html).
* [Bite Size](https://github.com/zulip/zulip/labels/bite%20size):
Smaller projects that might be a great first contribution.
* [Documentation](https://github.com/zulip/zulip/labels/documentation):
The Zulip project loves contributions of new documentation.
* [Help Wanted](https://github.com/zulip/zulip/labels/help%20wanted):
A broader list of projects that nobody is currently working on.
* [Platform support](https://github.com/zulip/zulip/labels/Platform%20support).
These are open issues about making it possible to install Zulip on a wider
range of platforms.
* [Bugs](https://github.com/zulip/zulip/labels/bug). Open bugs.
* [Feature requests](https://github.com/zulip/zulip/labels/enhancement).
Browsing this list can be a great way to find feature ideas to implement that
other Zulip users are excited about.
* [Platform support](https://github.com/zulip/zulip/labels/Platform%20support):
These are open issues about making it possible to install Zulip on a
wider range of platforms.
* [Bugs](https://github.com/zulip/zulip/labels/bug): Open bugs.
* [Feature requests](https://github.com/zulip/zulip/labels/enhancement):
Browsing this list can be a great way to find feature ideas to
implement that other Zulip users are excited about.
* [2016 roadmap milestone](http://zulip.readthedocs.io/en/latest/roadmap.html): The
projects that are [priorities for the Zulip project](https://zulip.readthedocs.io/en/latest/roadmap.html). These are great projects if you're looking to make an impact.
If you're excited about helping with an open issue, just post on the
conversation thread that you're working on it. You're encouraged to
@@ -123,7 +156,7 @@ looking at the new feature tutorial and coding style guidelines on
ReadTheDocs.
Feedback on how to make this development process more efficient, fun,
and friendly to new contributors is very welcome! Just shoot an email
and friendly to new contributors is very welcome! Just send an email
to the Zulip Developers list with your thoughts.
License

View File

@@ -56,14 +56,24 @@ These instructions should be followed as root.
You will eventually want to get a properly signed certificate (and
note that at present the Zulip desktop app doesn't support
self-signed certificates), but this will let you finish the
installation process.
installation process. You can get a free properly signed
certificate from the new [Letsencrypt](https://letsencrypt.org/)
service, by following their [nginx
instructions](https://letsencrypt.readthedocs.io/en/latest/using.html#nginx).
When you do get an actual certificate, you will need to install as
/etc/ssl/certs/zulip.combined-chain.crt the full certificate
authority chain, not just the certificate; see the section on "SSL
certificate chains" [in the nginx
docs](http://nginx.org/en/docs/http/configuring_https_servers.html)
for how to do this:
(2) Download [the latest built server tarball](https://www.zulip.com/dist/releases/zulip-server-latest.tar.gz)
and unpack it to `/root/zulip`, e.g.
```
wget https://www.zulip.com/dist/releases/zulip-server-latest.tar.gz
tar -xf zulip-server-latest.tar.gz
mv zulip-server-1.3.6 /root/zulip
mkdir -p /root/zulip && tar -xf zulip-server-latest.tar.gz --directory=/root/zulip --strip-components=1
```
(3) Run
@@ -138,8 +148,11 @@ need to do some additional setup documented in the `settings.py` template:
* For Google authentication, you need to follow the configuration
instructions around `GOOGLE_OAUTH2_CLIENT_ID` and `GOOGLE_CLIENT_ID`.
* For Email authentication, you will need to follow the configuration
instructions around outgoing SMTP from Django.
instructions for outgoing SMTP from Django. You can use `manage.py
send_test_email username@example.com` to test whether you've
successfully configured outgoing SMTP.
You should be able to login now. If you get an error, check
`/var/log/zulip/errors.log` for a traceback, and consult the next
@@ -313,15 +326,17 @@ only supports talking to servers with a properly signed SSL
certificate, so you may find that you get a blank screen when you
connect to a Zulip server using a self-signed certificate.
The Zulip iOS and Android apps in their respective stores don't yet
support talking to non-zulip.com servers; the iOS app is waiting on
Apple's app store review, while the Android app is waiting on someone
to do the small project of adding a field to specify what Zulip server
to talk to.
The Zulip Android app in the Google Play store doesn't yet support
talking to non-zulip.com servers (and the iOS one doesn't support
Google auth SSO against non-zulip.com servers; there's a design for
how to fix that which wouldn't be a ton of work to implement). If you
are interested in helping out with the Zulip mobile apps, shoot an
email to zulip-devel@googlegroups.com and the maintainers can guide
you on how to help.
These issues will likely all be addressed in the coming weeks; make
sure to join the zulip-announce@googlegroups.com list so that you can
receive the announcements when these become available.
For announcements about improvements to the apps, make sure to join
the zulip-announce@googlegroups.com list so that you can receive the
announcements when these become available.
(5) All the other features: Hotkeys, emoji, search filters,
@-mentions, etc. Zulip has lots of great features, make sure your
@@ -343,14 +358,10 @@ upgrade.
* To upgrade to a new version of the zulip server, download the
appropriate release tarball from
https://www.zulip.com/dist/releases/ to a path readable by the zulip
user (e.g. /home/zulip), and then run as root:
https://www.zulip.com/dist/releases/ and then run as root:
```
/home/zulip/deployments/current/scripts/upgrade-zulip zulip-server-VERSION.tar.gz
```
Be sure to download to a path readable by the Zulip user (see
https://github.com/zulip/zulip/issues/208 for details on this
issue) but then run the upgrade as root.
The upgrade process will shut down the service, run `apt-get
upgrade`, a puppet apply, and any database migrations, and then
@@ -381,7 +392,7 @@ upgrade.
* The Zulip upgrade process works by creating a new deployment under
/home/zulip/deployments/ containing a complete copy of the Zulip
server code, and then moving the symlinks at
`/home/zulip/deployments/current` and /root/zulip` as part of the
`/home/zulip/deployments/current` and `/root/zulip` as part of the
upgrade process. This means that if the new version isn't working,
you can quickly downgrade to the old version by using
`/home/zulip/deployments/<date>/scripts/restart-server` to return to
@@ -418,6 +429,351 @@ upgrade.
every Sunday early morning. See `/etc/cron.d/restart-zulip` for the
precise configuration.
### Backups for Zulip
There are several pieces of data that you might want to back up:
* The postgres database. That you can back up like any postgres
database; we have some example tooling for doing that incrementally
into S3 using [wal-e](https://github.com/wal-e/wal-e) in
`puppet/zulip_internal/manifests/postgres_common.pp` (that's what we
use for zulip.com's database backups). Note that this module isn't
part of the Zulip server releases since it's part of the zulip.com
configuration (see https://github.com/zulip/zulip/issues/293 for a
ticket about fixing this to make life easier for running backups).
* Any user-uploaded files. If you're using S3 as storage for file
uploads, this is backed up in S3, but if you have instead set
LOCAL_UPLOADS_DIR, any files uploaded by users (including avatars)
will be stored in that directory and you'll want to back it up.
* Your Zulip configuration including secrets from /etc/zulip/.
E.g. if you lose the value of secret_key, all users will need to login
again when you setup a replacement server since you won't be able to
verify their cookies; if you lose avatar_salt, any user-uploaded
avatars will need to be re-uploaded (since avatar filenames are
computed using a hash of avatar_salt and user's email), etc.
* The logs under /var/log/zulip can be handy to have backed up, but
they do get large on a busy server, and it's definitely
lower-priority.
#### Restoration
To restore from backups, the process is basically the reverse of the above:
* Install new server as normal by downloading a Zulip release tarball
and then using `scripts/setup/install`, you don't need
to run the `initialize-database` second stage which puts default
data into the database.
* Unpack to /etc/zulip the settings.py and secrets.conf files from your backups.
* Restore your database from the backup using wal-e; if you ran
`initialize-database` anyway above, you'll want to first
`scripts/setup/postgres-init-db` to drop the initial database first.
* If you're using local file uploads, restore those files to the path
specified by `settings.LOCAL_UPLOADS_DIR` and (if appropriate) any
logs.
* Start the server using scripts/restart-server
This restoration process can also be used to migrate a Zulip
installation from one server to another.
We recommend running a disaster recovery after you setup backups to
confirm that your backups are working; you may also want to monitor
that they are up to date using the Nagios plugin at:
`puppet/zulip_internal/files/nagios_plugins/check_postgres_backup`.
Contributions to more fully automate this process or make this section
of the guide much more explicit and detailed are very welcome!
#### Postgres streaming replication
Zulip has database configuration for using Postgres streaming
replication; you can see the configuration in these files:
* puppet/zulip_internal/manifests/postgres_slave.pp
* puppet/zulip_internal/manifests/postgres_master.pp
* puppet/zulip_internal/files/postgresql/*
Contribution of a step-by-step guide for setting this up (and moving
this configuration to be available in the main `puppet/zulip/` tree)
would be very welcome!
### Monitoring Zulip
The complete Nagios configuration (sans secret keys) used to
monitor zulip.com is available under `puppet/zulip_internal` in the
Zulip Git repository (those files are not installed in the release
tarballs).
The Nagios plugins used by that configuration are installed
automatically by the Zulip installation process in subdirectories
under `/usr/lib/nagios/plugins/`. The following is a summary of the
various Nagios plugins included with Zulip and what they check:
Application server and queue worker monitoring:
* check_send_receive_time (sends a test message through the system
between two bot users to check that end-to-end message sending works)
* check_rabbitmq_consumers and check_rabbitmq_queues (checks for
rabbitmq being down or the queue workers being behind)
* check_queue_worker_errors (checks for errors reported by the queue workers)
* check_worker_memory (monitors for memory leaks in queue workers)
* check_email_deliverer_backlog and check_email_deliverer_process
(monitors for whether outgoing emails are being sent)
Database monitoring:
* check_postgres_replication_lag (checks streaming replication is up
to date).
* check_postgres (checks the health of the postgres database)
* check_postgres_backup (checks backups are up to date; see above)
* check_fts_update_log (monitors for whether full-text search updates
are being processed)
Standard server monitoring:
* check_website_response.sh (standard HTTP check)
* check_debian_packages (checks apt repository is up to date)
If you're using these plugins, bug reports and pull requests to make
it easier to monitor Zulip and maintain it in production are
encouraged!
### Scalability of Zulip
This section attempts to address the considerations involved with
running Zulip with a large team (>1000 users).
* We recommend using a [remote postgres
database](#postgres-database-details) for isolation, though it is
not required. In the following, we discuss a relatively simple
configuration with two types of servers: application servers
(running Django, Tornado, RabbitMQ, Redis, Memcached, etc.) and
database servers.
* You can scale to a pretty large installation (O(~1000) concurrently
active users using it to chat all day) with just a single reasonably
large application server (e.g. AWS c3.2xlarge with 8 cores and 16GB
of RAM) sitting mostly idle (<10% CPU used and only 4GB of the 16GB
RAM actively in use). You can probably get away with half that
(e.g. c3.xlarge), but ~8GB of RAM is highly recommended at scale.
Beyond a 1000 active users, you will eventually want to increase the
memory cap in `memcached.conf` from the default 512MB to avoid high
rates of memcached misses.
* For the database server, we highly recommend SSD disks, and RAM is
the primary resource limitation. We have not aggressively tested
for the minimum resources required, but 8 cores with 30GB of RAM
(e.g. AWS's m3.2xlarge) should suffice; you may be able to get away
with less especially on the CPU side. The database load per user is
pretty optimized as long as `memcached` is working correctly. This
has not been tested, but from extrapolating the load profile, it
should be possible to scale a Zulip installation to 10,000s of
active users using a single large database server without doing
anything complicated like sharding the database.
* For reasonably high availability, it's easy to run a hot spare
application server and a hot spare database (using Postgres
streaming replication; see the section on configuring this). Be
sure to check out the section on backups if you're hoping to run a
spare application server; in particular you probably want to use the
S3 backend for storing user-uploaded files and avatars and will want
to make sure secrets are available on the hot spare.
* Zulip does not support dividing traffic for a given Zulip realm
between multiple application servers. There are two issues: you
need to share the memcached/redis/rabbitmq instance (these should
can be moved to a network service shared by multiple servers with a
bit of configuration) and the Tornado event system for pushing to
browsers currently has no mechanism for multiple frontend servers
(or event processes) talking to each other. One can probably get a
factor of 10 in a single server's scalability by [supporting
multiple tornado processes on a single
server](https://github.com/zulip/zulip/issues/372), which is also
likely the first part of any project to support exchanging events
amongst multiple servers.
Questions, concerns, and bug reports about this area of Zulip are very
welcome! This is an area we are hoping to improve.
### Security Model
This section attempts to document the Zulip security model. Since
this is new documentation, it likely does not cover every issue; if
there are details you're curious about, please feel free to ask
questions on the Zulip development mailing list (or if you think
you've found a security bug, please report it to support@zulip.com so
we can do a responsible security announcement).
#### Secure your Zulip server like your email server
* It's reasonable to think about security for a Zulip server like you
do security for a team email server -- only trusted administrators
within an organization should have shell access to the server.
In particular, anyone with root access to a Zulip application server
or Zulip database server, or with access to the `zulip` user on a
Zulip application server, has complete control over the Zulip
installation and all of its data (so they can read messages, modify
history, etc.). It would be difficult or impossible to avoid this,
because the server needs access to the data to support features
expected of a group chat system like the ability to search the
entire message history, and thus someone with control over the
server has access to that data as well.
#### Encryption and Authentication
* Traffic between clients (web, desktop and mobile) and the Zulip is
encrypted using HTTPS. By default, all Zulip services talk to each
other either via a localhost connection or using an encrypted SSL
connection.
* The preferred way to login to Zulip is using an SSO solution like
Google Auth, LDAP, or similar. Zulip stores user passwords using
the standard PBKDF2 algorithm. Password strength is checked and
weak passwords are visually discouraged using the zxcvbn library,
but Zulip does not by default have strong requirements on user
password strength. Modify `static/js/common.js` to adjust the
password strength requirements (Patches welcome to make controlled
by an easy setting!).
* Zulip requires CSRF tokens in all interactions with the web API to
prevent CSRF attacks.
#### Messages and History
* Zulip message content is rendering using a specialized Markdown
parser which escapes content to protect against cross-site scripting
attacks.
* Zulip supports both public streams and private ("invite-only")
streams. Any Zulip user can join any public stream in the realm
(and can view the complete message of any public stream history
without joining the stream).
* Users who are not members of a private stream cannot read messages
on the stream, send messages to the stream, or join the stream, even
if they are a Zulip administrator. However, any member of a private
stream can invite other users to the stream. When a new user joins
a private stream, they can see future messages sent to the stream,
but they do not receive access to the stream's message history.
* Zulip supports editing the content or topics of messages that have
already been sent (and even updating the topic of messages sent by
other users when editing the topic of the overall thread).
While edited messages are synced immediately to open browser
windows, editing messages is not a safe way to redact secret content
(e.g. a password) unintentionally shared via Zulip, because other
users may have seen and saved the content of the original message
(for example, they could have taken a screenshot immediately after
you sent the message, or have an API tool recording all messages
they receive).
Zulip stores and sends to clients the content of every historical
version of a message, so that future versions of Zulip could support
displaying the diffs between previous versions.
#### Users and Bots
* There are three types of users in a Zulip realm: Administrators,
normal users, and bots. Administrators have the ability to
deactivate and reactivate other human and bot users, delete streams,
add/remove administrator privileges, as well as change configuration
for the overall realm (e.g. whether an invitation is required to
join the realm). Being a Zulip administrator does not provide the
ability to interact with other users' private messages or the
messages sent private streams to which the administrator is not
subscribed. However, a Zulip administrator subscribed to a stream
can toggle whether that stream is public or private.
* Every Zulip user has an API key, available on the settings page.
This API key can be used to do essentially everything the user can
do; for that reason, users should keep their API key safe. Users
can rotate their own API key if it is accidentally compromised.
* To properly remove a user's access to a Zulip team, it does not
suffice to change their password or deactivate their account in the
SSO system, since neither of those prevents authenticating with the
user's API key or those of bots the user has created. Instead, you
should deactivate the user's account in the Zulip administration
interface (/#administration); this will automatically also
deactivate any bots the user had created.
* The Zulip mobile apps authenticate to the server by sending the
user's password and retrieving the user's API key; the apps then use
the API key to authenticate all future interactions with the site.
Thus, if a user's phone is lost, in addition to changing passwords,
you should rotate the user's Zulip API key.
* Zulip bots are used for integrations. A Zulip bot can do everything
a normal user in the realm can do including reading other, with a
few exceptions (e.g. a bot cannot login to the web application or
create other bots). In particular, with the API key for a Zulip
bot, one can read any message sent to a public stream in that bot's
realm. A likely future feature for Zulip is [limited bots that can
only send messages](https://github.com/zulip/zulip/issues/373).
* Certain Zulip bots can be marked as "API super users"; these special
bots have the ability to send messages that appear to have been sent
by another user (an important feature for implementing integrations
like the Jabber, IRC, and Zephyr mirrors).
#### User-uploaded content
* Zulip supports user-uploaded files; ideally they should be hosted
from a separate domain from the main Zulip server to protect against
various same-domain attacks (e.g. zulip-user-content.example.com)
using the S3 integration.
The URLs of user-uploaded files are secret; if you are using the
"local file upload" integration, anyone with the URL of an uploaded
file can access the file. This means the local uploads integration
is vulnerable to a subtle attack where if a user clicks on a link in
a secret .PDF or .HTML file that had been uploaded to Zulip, access
to the file might be leaked to the other server via the Referrer
header (see https://github.com/zulip/zulip/issues/320).
The Zulip S3 file upload integration is relatively safe against that
attack, because the URLs of files presented to users don't host the
content. Instead, the S3 integration checks the user has a valid
Zulip session in the relevant realm, and if so then redirects the
browser to a one-time S3 URL that expires a short time later.
Keeping the URL secret is still important to avoid other users in
the Zulip realm from being able to access the file.
* Zulip supports using the Camo image proxy to proxy content like
inline image previews that can be inserted into the Zulip message
feed by other users over HTTPS.
* By default, Zulip will provide image previews inline in the body of
messages when a message contains a link to an image. You can
control this using the `INLINE_IMAGE_PREVIEW` setting.
#### Final notes and security response
If you find some aspect of Zulip that seems inconsistent with this
security model, please report it to support@zulip.com so that we can
investigate and coordinate an appropriate security release if needed.
Zulip security announcements will be sent to
zulip-announce@googlegroups.com, so you should subscribe if you are
running Zulip in production.
Remote User SSO Authentication
==============================
@@ -508,3 +864,138 @@ understanding what's going on as you try to debug:
Again, most issues with this setup tend to be subtle issues with the
hostname/DNS side of the configuration. Suggestions for how to
improve this SSO setup documentation are very welcome!
Postgres database details
=========================
#### Remote Postgres database
This is a bit annoying to setup, but you can configure Zulip to use a
dedicated postgres server by setting the `REMOTE_POSTGRES_HOST`
variable in /etc/zulip/settings.py, and configuring Postgres
certificate authentication (see
http://www.postgresql.org/docs/9.1/static/ssl-tcp.html and
http://www.postgresql.org/docs/9.1/static/libpq-ssl.html for
documentation on how to set this up and deploy the certificates) to
make the DATABASES configuration in `zproject/settings.py` work (or
override that configuration).
If you want to use a remote Postgresql database, you should configure
the information about the connection with the server. You need a user
called "zulip" in your database server. You can configure these
options in /etc/zulip/settings.py:
* REMOTE_POSTGRES_HOST: Name or IP address of the remote host
* REMOTE_POSTGRES_SSLMODE: SSL Mode used to connect to the server, different options you can use are:
* disable: I don't care about security, and I don't want to pay the overhead of encryption.
* allow: I don't care about security, but I will pay the overhead of encryption if the server insists on it.
* prefer: I don't care about encryption, but I wish to pay the overhead of encryption if the server supports it.
* require: I want my data to be encrypted, and I accept the overhead. I trust that the network will make sure I always connect to the server I want.
* verify-ca: I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server that I trust.
* verify-full: I want my data encrypted, and I accept the overhead. I want to be sure that I connect to a server I trust, and that it's the one I specify.
Then you should specify the password of the user zulip for the database in /etc/zulip/zulip-secrets.conf:
```
postgres_password = xxxx
```
Finally, you can stop your database on the Zulip server via:
```
sudo service postgresql stop
sudo update-rc.d postgresql disable
```
In future versions of this feature, we'd like to implement and
document how to the remote postgres database server itself
automatically by using the Zulip install script with a different set
of puppet manifests than the all-in-one feature; if you're interested
in working on this, post to the Zulip development mailing list and we
can give you some tips.
#### Debugging postgres database issues
When debugging postgres issues, in addition to the standard `pg_top`
tool, often it can be useful to use this query:
```
SELECT procpid,waiting,query_start,current_query FROM pg_stat_activity ORDER BY procpid;
```
which shows the currently running backends and their activity. This is
similar to the pg_top output, with the added advantage of showing the
complete query, which can be valuable in debugging.
To stop a runaway query, you can run `SELECT pg_cancel_backend(pid
int)` or `SELECT pg_terminate_backend(pid int)` as the 'postgres'
user. The former cancels the backend's current query and the latter
terminates the backend process. They are implemented by sending SIGINT
and SIGTERM to the processes, respectively. We recommend against
sending a Postgres process SIGKILL. Doing so will cause the database
to kill all current connections, roll back any pending transactions,
and enter recovery mode.
#### Stopping the Zulip postgres database
To start or stop postgres manually, use the pg_ctlcluster command:
```
pg_ctlcluster 9.1 [--force] main {start|stop|restart|reload}
```
By default, using stop uses "smart" mode, which waits for all clients
to disconnect before shutting down the database. This can take
prohibitively long. If you use the --force option with stop,
pg_ctlcluster will try to use the "fast" mode for shutting
down. "Fast" mode is described by the manpage thusly:
With the --force option the "fast" mode is used which rolls back all
active transactions, disconnects clients immediately and thus shuts
down cleanly. If that does not work, shutdown is attempted again in
"immediate" mode, which can leave the cluster in an inconsistent state
and thus will lead to a recovery run at the next start. If this still
does not help, the postmaster process is killed. Exits with 0 on
success, with 2 if the server is not running, and with 1 on other
failure conditions. This mode should only be used when the machine is
about to be shut down.
Many database parameters can be adjusted while the database is
running. Just modify /etc/postgresql/9.1/main/postgresql.conf and
issue a reload. The logs will note the change.
#### Debugging issues starting postgres
pg_ctlcluster often doesn't give you any information on why the
database failed to start. It may tell you to check the logs, but you
won't find any information there. pg_ctlcluster runs the following
command underneath when it actually goes to start Postgres:
```
/usr/lib/postgresql/9.1/bin/pg_ctl start -D /var/lib/postgresql/9.1/main -s -o '-c config_file="/etc/postgresql/9.1/main/postgresql.conf"'
```
Since pg_ctl doesn't redirect stdout or stderr, running the above can
give you better diagnostic information. However, you might want to
stop Postgres and restart it using pg_ctlcluster after you've debugged
with this approach, since it does bypass some of the work that
pg_ctlcluster does.
#### Postgres Vacuuming alerts
The `autovac_freeze` postgres alert from `check_postgres` is
particularly important. This alert indicates that the age (in terms
of number of transactions) of the oldest transaction id (XID) is
getting close to the `autovacuum_freeze_max_age` setting. When the
oldest XID hits that age, Postgres will force a VACUUM operation,
which can often lead to sudden downtime until the operation finishes.
If it did not do this and the age of the oldest XID reached 2 billion,
transaction id wraparound would occur and there would be data loss.
To clear the nagios alert, perform a `VACUUM` in each indicated
database as a database superuser (`postgres`).
See
http://www.postgresql.org/docs/9.1/static/routine-vacuuming.html#VACUUM-FOR-WRAPAROUND
for more details on postgres vacuuming.

View File

@@ -63,16 +63,16 @@ Copyright: 2006 Otheus Shelling
License: GPL-2.0
Comment: Not linked.
Files: puppet/zulip_internal/files/nagios_plugins/check_debian_packages
Files: puppet/zulip/files/nagios_plugins/zulip_base/check_debian_packages
Copyright: 2005 Francesc Guasch
License: GPL-2.0
Comment: Not linked.
Files: puppet/zulip_internal/files/nagios_plugins/check_postgres.pl
Files: puppet/zulip/files/nagios_plugins/zulip_postgres_appdb/check_postgres.pl
Copyright: 2007-2015 Greg Sabino Mullane
License: BSD-2-Clause
Files: puppet/zulip_internal/files/nagios_plugins/check_website_response.sh
Files: puppet/zulip/files/nagios_plugins/zulip_nagios_server/check_website_response.sh
Copyright: 2011 Chris Freeman
License: GPL-2.0
@@ -229,10 +229,6 @@ Files: tools/jslint/jslint.js
Copyright: 2002 Douglas Crockford
License: XXX-good-not-evil
Files: tools/python-proxy
Copyright: 2009 F.bio Domingues
License: Expat
Files: tools/review
Copyright: 2010 Ksplice, Inc.
License: Apache-2.0
@@ -246,6 +242,10 @@ Files: zerver/lib/ccache.py
Copyright: 2013 David Benjamin and Alan Huang
License: Expat
Files: zerver/lib/decorator.py zerver/management/commands/runtornado.py scripts/setup/generate_secrets.py
Copyright: Django Software Foundation and individual contributors
License: BSD-3-Clause
Files: frontend_tests/casperjs/*
Copyright: 2011-2012 Nicolas Perriault
Joyent, Inc. and other Node contributors

52
Vagrantfile vendored
View File

@@ -2,6 +2,11 @@
VAGRANTFILE_API_VERSION = "2"
def command?(name)
`which #{name}`
$?.success?
end
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# For LXC. VirtualBox hosts use a different box, described below.
@@ -13,6 +18,49 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.synced_folder ".", "/vagrant", disabled: true
config.vm.synced_folder ".", "/srv/zulip"
proxy_config_file = ENV['HOME'] + "/.zulip-vagrant-config"
if File.file?(proxy_config_file)
http_proxy = https_proxy = no_proxy = ""
IO.foreach(proxy_config_file) do |line|
line.chomp!
key, value = line.split(nil, 2)
case key
when /^([#;]|$)/; # ignore comments
when "HTTP_PROXY"; http_proxy = value
when "HTTPS_PROXY"; https_proxy = value
when "NO_PROXY"; no_proxy = value
end
end
if Vagrant.has_plugin?("vagrant-proxyconf")
if http_proxy != ""
config.proxy.http = http_proxy
end
if https_proxy != ""
config.proxy.https = https_proxy
end
if https_proxy != ""
config.proxy.no_proxy = no_proxy
end
end
end
# Specify LXC provider before VirtualBox provider so it's preferred.
config.vm.provider "lxc" do |lxc|
if command? "lxc-ls"
LXC_VERSION = `lxc-ls --version`.strip unless defined? LXC_VERSION
if LXC_VERSION >= "1.1.0"
# Allow start without AppArmor, otherwise Box will not Start on Ubuntu 14.10
# see https://github.com/fgrehm/vagrant-lxc/issues/333
lxc.customize 'aa_allow_incomplete', 1
end
if LXC_VERSION >= "2.0.0"
lxc.backingstore = 'dir'
end
end
end
config.vm.provider "virtualbox" do |vb, override|
override.vm.box = "ubuntu/trusty64"
# 2GiB seemed reasonable here. The VM OOMs with only 1024MiB.
@@ -22,9 +70,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
$provision_script = <<SCRIPT
set -x
set -e
sudo apt-get update
sudo apt-get install -y python-pbs
python /srv/zulip/provision.py
/usr/bin/python /srv/zulip/provision.py
SCRIPT
config.vm.provision "shell",

View File

@@ -22,7 +22,7 @@ class Command(BaseCommand):
# Calculate 10min, 2hrs, 12hrs, 1day, 2 business days (TODO business days), 1 week bucket of stats
hour_buckets = [0.16, 2, 12, 24, 48, 168]
user_info = defaultdict(dict)
user_info = defaultdict(dict) # type: Dict[str, Dict[float, List[str]]]
for last_presence in users:
if last_presence.status == UserPresence.IDLE:
@@ -37,13 +37,13 @@ class Command(BaseCommand):
user_info[last_presence.user_profile.realm.domain][bucket].append(last_presence.user_profile.email)
for realm, buckets in user_info.items():
print("Realm %s" % realm)
print("Realm %s" % (realm,))
for hr, users in sorted(buckets.items()):
print("\tUsers for %s: %s" % (hr, len(users)))
statsd.gauge("users.active.%s.%shr" % (statsd_key(realm, True), statsd_key(hr, True)), len(users))
# Also do stats for how many users have been reading the app.
users_reading = UserActivity.objects.select_related().filter(query="/json/update_message_flags")
users_reading = UserActivity.objects.select_related().filter(query="/json/messages/flags")
user_info = defaultdict(dict)
for activity in users_reading:
for bucket in hour_buckets:
@@ -52,7 +52,7 @@ class Command(BaseCommand):
if datetime.now(activity.last_visit.tzinfo) - activity.last_visit < timedelta(hours=bucket):
user_info[activity.user_profile.realm.domain][bucket].append(activity.user_profile.email)
for realm, buckets in user_info.items():
print("Realm %s" % realm)
print("Realm %s" % (realm,))
for hr, users in sorted(buckets.items()):
print("\tUsers reading for %s: %s" % (hr, len(users)))
statsd.gauge("users.reading.%s.%shr" % (statsd_key(realm, True), statsd_key(hr, True)), len(users))

View File

@@ -27,15 +27,15 @@ def compute_stats(log_level):
"bitcoin@mit.edu", "lp@mit.edu", "clocks@mit.edu",
"root@mit.edu", "nagios@mit.edu",
"www-data|local-realm@mit.edu"])
user_counts = {}
user_counts = {} # type: Dict[str, Dict[str, int]]
for m in mit_query.select_related("sending_client", "sender"):
email = m.sender.email
user_counts.setdefault(email, {})
user_counts[email].setdefault(m.sending_client.name, 0)
user_counts[email][m.sending_client.name] += 1
total_counts = {}
total_user_counts = {}
total_counts = {} # type: Dict[str, int]
total_user_counts = {} # type: Dict[str, int]
for email, counts in user_counts.items():
total_user_counts.setdefault(email, 0)
for client_name, count in counts.items():
@@ -44,9 +44,9 @@ def compute_stats(log_level):
total_user_counts[email] += count
logging.debug("%40s | %10s | %s" % ("User", "Messages", "Percentage Zulip"))
top_percents = {}
top_percents = {} # type: Dict[int, float]
for size in [10, 25, 50, 100, 200, len(total_user_counts.keys())]:
top_percents[size] = 0
top_percents[size] = 0.0
for i, email in enumerate(sorted(total_user_counts.keys(),
key=lambda x: -total_user_counts[x])):
percent_zulip = round(100 - (user_counts[email].get("zephyr_mirror", 0)) * 100. /

View File

@@ -1,4 +1,5 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from zerver.lib.statistics import seconds_usage_between

View File

@@ -1,4 +1,5 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import datetime
@@ -28,7 +29,7 @@ class Command(BaseCommand):
UserActivity.objects.filter(user_profile__realm=realm,
user_profile__is_active=True,
last_visit__gt=activity_cutoff,
query="/json/update_pointer",
query="/json/users/me/pointer",
client__name="website")]
def messages_sent_by(self, user, days_ago):

View File

@@ -1,4 +1,7 @@
from __future__ import absolute_import
from __future__ import division
from typing import Any, Dict, List, Tuple
from django.db import connection
from django.template import RequestContext, loader
from django.utils.html import mark_safe
@@ -75,7 +78,7 @@ def get_realm_day_counts():
rows = dictfetchall(cursor)
cursor.close()
counts = defaultdict(dict)
counts = defaultdict(dict) # type: Dict[str, Dict[int, int]]
for row in rows:
counts[row['domain']][row['age']] = row['cnt']
@@ -137,7 +140,8 @@ def realm_summary_table(realm_minutes):
'/json/send_message',
'send_message_backend',
'/api/v1/send_message',
'/json/update_pointer'
'/json/update_pointer',
'/json/users/me/pointer'
)
AND
last_visit > now() - interval '1 day'
@@ -166,8 +170,9 @@ def realm_summary_table(realm_minutes):
ua.query in (
'/json/send_message',
'send_message_backend',
'/api/v1/send_message',
'/json/update_pointer'
'/api/v1/send_message',
'/json/update_pointer',
'/json/users/me/pointer'
)
GROUP by realm.id, up.email
HAVING max(last_visit) between
@@ -187,7 +192,8 @@ def realm_summary_table(realm_minutes):
'/json/send_message',
'/api/v1/send_message',
'send_message_backend',
'/json/update_pointer'
'/json/update_pointer',
'/json/users/me/pointer'
)
AND
up.realm_id = realm.id
@@ -619,7 +625,8 @@ def raw_user_activity_table(records):
return make_table(title, cols, rows)
def get_user_activity_summary(records):
summary = {}
# type: (Any) -> Any
summary = {} # type: Dict[str, Dict[str, Any]]
def update(action, record):
if action not in summary:
summary[action] = dict(
@@ -654,7 +661,7 @@ def get_user_activity_summary(records):
update('website', record)
if ('send_message' in query) or re.search('/api/.*/external/.*', query):
update('send', record)
if query in ['/json/update_pointer', '/api/v1/update_pointer']:
if query in ['/json/update_pointer', '/json/users/me/pointer', '/api/v1/update_pointer']:
update('pointer', record)
update(client, record)
@@ -816,9 +823,9 @@ def realm_user_summary_table(all_records, admin_emails):
@zulip_internal
def get_realm_activity(request, realm):
data = []
all_records = {}
all_user_records = {}
# type: (Any, Any) -> Any
data = [] # type: List[Tuple[str, str]]
all_user_records = {} # type: Dict[str, Any]
try:
admins = get_realm(realm).get_admin_users()
@@ -828,8 +835,7 @@ def get_realm_activity(request, realm):
admin_emails = {admin.email for admin in admins}
for is_bot, page_title in [(False, 'Humans'), (True, 'Bots')]:
all_records = get_user_activity_records_for_realm(realm, is_bot)
all_records = list(all_records)
all_records = list(get_user_activity_records_for_realm(realm, is_bot))
user_records, content = realm_user_summary_table(all_records, admin_emails)
all_user_records.update(user_records)
@@ -861,7 +867,7 @@ def get_realm_activity(request, realm):
def get_user_activity(request, email):
records = get_user_activity_records_for_email(email)
data = []
data = [] # type: List[Tuple[str, str]]
user_summary = get_user_activity_summary(records)
content = user_activity_summary_table(user_summary)

View File

@@ -8,3 +8,4 @@ include examples/unsubscribe
include examples/list-members
include examples/list-subscriptions
include examples/print-messages
include examples/recent-messages

View File

@@ -21,6 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import sys
from os import path
import optparse
@@ -46,9 +47,9 @@ parser.add_option('--new-short-name')
client = zulip.init_from_options(options)
print client.create_user({
print(client.create_user({
'email': options.new_email,
'password': options.new_password,
'full_name': options.new_full_name,
'short_name': options.new_short_name
})
}))

View File

@@ -21,6 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import sys
import os
import optparse
@@ -53,4 +54,4 @@ if options.subject != "":
message_data["subject"] = options.subject
if options.content != "":
message_data["content"] = options.content
print client.update_message(message_data)
print(client.update_message(message_data))

View File

@@ -21,6 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import sys
import os
import optparse
@@ -43,4 +44,4 @@ parser.add_option_group(zulip.generate_option_group(parser))
client = zulip.init_from_options(options)
print client.get_streams(include_public=True, include_subscribed=False)
print(client.get_streams(include_public=True, include_subscribed=False))

View File

@@ -21,6 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import sys
import os
import optparse
@@ -42,4 +43,4 @@ parser.add_option_group(zulip.generate_option_group(parser))
client = zulip.init_from_options(options)
for user in client.get_members()["members"]:
print user["full_name"], user["email"]
print(user["full_name"], user["email"])

View File

@@ -21,6 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import sys
import os
import optparse
@@ -42,4 +43,4 @@ parser.add_option_group(zulip.generate_option_group(parser))
client = zulip.init_from_options(options)
print client.list_subscriptions()
print(client.list_subscriptions())

View File

@@ -21,6 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import sys
import os
import optparse
@@ -43,7 +44,7 @@ parser.add_option_group(zulip.generate_option_group(parser))
client = zulip.init_from_options(options)
def print_event(event):
print event
print(event)
# This is a blocking call, and will continuously poll for new events
# Note also the filter here is messages to the stream Denmark; if you

View File

@@ -21,6 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import sys
import os
import optparse
@@ -43,7 +44,7 @@ parser.add_option_group(zulip.generate_option_group(parser))
client = zulip.init_from_options(options)
def print_message(message):
print message
print(message)
# This is a blocking call, and will continuously poll for new messages
client.call_on_each_message(print_message)

View File

@@ -21,6 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import sys
import os
import optparse
@@ -42,4 +43,4 @@ parser.add_option_group(zulip.generate_option_group(parser))
client = zulip.init_from_options(options)
print client.get_messages({})
print(client.get_messages({}))

61
api/examples/recent-messages Executable file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
# Copyright © 2012 Zulip, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import sys
import os
import json
import optparse
usage = """recent-messages [options] --count=<no. of previous messages> --user=<sender's email address> --api-key=<sender's api key>
Prints out last count messages recieved by the indicated bot or user
Example: recent-messages --count=101 --user=username@example.com --api-key=a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5
You can omit --user and --api-key arguments if you have a properly set up ~/.zuliprc
"""
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import zulip
parser = optparse.OptionParser(usage=usage)
parser.add_option('--count', default=100)
parser.add_option_group(zulip.generate_option_group(parser))
(options, args) = parser.parse_args()
client = zulip.init_from_options(options)
req = {
'narrow': [["stream", "Denmark"]],
'num_before': options.count,
'num_after': 0,
'anchor': 1000000000,
'apply_markdown': False
}
old_messages = client.do_api_query(req, zulip.API_VERSTRING + 'messages', method='GET')
if 'messages' in old_messages:
for message in old_messages['messages']:
print(json.dumps(message, indent=4))
else:
print([])

View File

@@ -21,6 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import sys
import os
import optparse
@@ -54,4 +55,4 @@ message_data = {
"subject": options.subject,
"to": args,
}
print client.send_message(message_data)
print(client.send_message(message_data))

View File

@@ -21,6 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import sys
import os
import optparse
@@ -45,8 +46,8 @@ parser.add_option('--streams', default='')
client = zulip.init_from_options(options)
if options.streams == "":
print >>sys.stderr, "Usage:", parser.usage
print("Usage:", parser.usage, file=sys.stderr)
sys.exit(1)
print client.add_subscriptions([{"name": stream_name} for stream_name in
options.streams.split()])
print(client.add_subscriptions([{"name": stream_name} for stream_name in
options.streams.split()]))

View File

@@ -21,6 +21,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import sys
import os
import optparse
@@ -45,7 +46,7 @@ parser.add_option('--streams', default='')
client = zulip.init_from_options(options)
if options.streams == "":
print >>sys.stderr, "Usage:", parser.usage
print("Usage:", parser.usage, file=sys.stderr)
sys.exit(1)
print client.remove_subscriptions(options.streams.split())
print(client.remove_subscriptions(options.streams.split()))

View File

@@ -30,6 +30,7 @@
#
# python-dateutil is a dependency for this script.
from __future__ import print_function
import base64
from datetime import datetime, timedelta
@@ -37,16 +38,16 @@ import json
import logging
import os
import time
import urllib2
from six.moves import urllib
import sys
try:
import dateutil.parser
import dateutil.tz
except ImportError, e:
print >>sys.stderr, e
print >>sys.stderr, "Please install the python-dateutil package."
except ImportError as e:
print(e, file=sys.stderr)
print("Please install the python-dateutil package.", file=sys.stderr)
exit(1)
sys.path.insert(0, os.path.dirname(__file__))
@@ -74,8 +75,8 @@ def fetch_from_asana(path):
headers = {"Authorization": "Basic %s" % auth}
url = "https://app.asana.com/api/1.0" + path
request = urllib2.Request(url, None, headers)
result = urllib2.urlopen(request)
request = urllib.request.Request(url, None, headers)
result = urllib.request.urlopen(request)
return json.load(result)
@@ -189,7 +190,7 @@ def since():
timestamp = float(datestring)
max_timestamp_processed = datetime.fromtimestamp(timestamp)
logging.info("Reading from resume file: " + datestring)
except (ValueError,IOError) as e:
except (ValueError, IOError) as e:
logging.warn("Could not open resume file: %s" % (
e.message or e.strerror,))
max_timestamp_processed = default_since()

View File

@@ -26,6 +26,7 @@
# or preferably on a server.
# You may need to install the python-requests library.
from __future__ import absolute_import
import requests
import logging
import time
@@ -33,7 +34,8 @@ import re
import sys
import os
from datetime import datetime, timedelta
from HTMLParser import HTMLParser
from six.moves.html_parser import HTMLParser
import six
sys.path.insert(0, os.path.dirname(__file__))
import zulip_basecamp_config as config
@@ -80,7 +82,7 @@ def check_permissions():
# builds the message dict for sending a message with the Zulip API
def build_message(event):
if not (event.has_key('bucket') and event.has_key('creator') and event.has_key('html_url')):
if not ('bucket' in event and 'creator' in event and 'html_url' in event):
logging.error("Perhaps the Basecamp API changed behavior? "
"This event doesn't have the expected format:\n%s" %(event,))
return None
@@ -92,7 +94,7 @@ def build_message(event):
action = htmlParser.unescape(re.sub(r"<[^<>]+>", "", event.get('action', '')))
target = htmlParser.unescape(event.get('target', ''))
# Some events have "excerpts", which we blockquote
excerpt = htmlParser.unescape(event.get('excerpt',''))
excerpt = htmlParser.unescape(event.get('excerpt', ''))
if excerpt.strip() == "":
message = '**%s** %s [%s](%s).' % (event['creator']['name'], action, target, event['html_url'])
else:
@@ -116,13 +118,13 @@ def run_mirror():
since = re.search(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}-\d{2}:\d{2}", since)
assert since, "resume file does not meet expected format"
since = since.string
except (AssertionError,IOError) as e:
except (AssertionError, IOError) as e:
logging.warn("Could not open resume file: %s" % (e.message or e.strerror,))
since = (datetime.utcnow() - timedelta(hours=config.BASECAMP_INITIAL_HISTORY_HOURS)).isoformat() + "-00:00"
try:
# we use an exponential backoff approach when we get 429 (Too Many Requests).
sleepInterval = 1
while 1:
while True:
time.sleep(sleepInterval)
response = requests.get("https://basecamp.com/%s/api/v1/events.json" % (config.BASECAMP_ACCOUNT_ID),
params={'since': since},
@@ -170,7 +172,7 @@ def run_mirror():
if __name__ == "__main__":
if not isinstance(config.RESUME_FILE, basestring):
if not isinstance(config.RESUME_FILE, six.string_types):
sys.stderr("RESUME_FILE path not given; refusing to continue")
check_permissions()
if config.LOG_FILE:

View File

@@ -29,6 +29,8 @@
#
# python-dateutil is a dependency for this script.
from __future__ import print_function
from __future__ import absolute_import
import requests
import logging
import time
@@ -36,13 +38,14 @@ import sys
import os
from datetime import datetime, timedelta
import six
try:
import dateutil.parser
except ImportError, e:
print >>sys.stderr, e
print >>sys.stderr, "Please install the python-dateutil package."
except ImportError as e:
print(e, file=sys.stderr)
print("Please install the python-dateutil package.", file=sys.stderr)
exit(1)
sys.path.insert(0, os.path.dirname(__file__))
@@ -110,7 +113,7 @@ def handle_event(event):
project_name = raw_props.get('name')
project_repo_type = raw_props.get('scm_type')
url = make_url("projects/%s" % project_link)
url = make_url("projects/%s" % (project_link,))
scm = "of type %s" % (project_repo_type,) if project_repo_type else ""
@@ -271,13 +274,13 @@ def run_mirror():
else:
timestamp = int(timestamp, 10)
since = datetime.fromtimestamp(timestamp)
except (ValueError,IOError) as e:
except (ValueError, IOError) as e:
logging.warn("Could not open resume file: %s" % (e.message or e.strerror,))
since = default_since()
try:
sleepInterval = 1
while 1:
while True:
events = make_api_call("activity")[::-1]
if events is not None:
sleepInterval = 1
@@ -314,7 +317,7 @@ def check_permissions():
sys.stderr(e)
if __name__ == "__main__":
if not isinstance(config.RESUME_FILE, basestring):
if not isinstance(config.RESUME_FILE, six.string_types):
sys.stderr("RESUME_FILE path not given; refusing to continue")
check_permissions()
if config.LOG_FILE:

View File

@@ -29,13 +29,14 @@
# For example:
# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
from __future__ import absolute_import
import os
import sys
import subprocess
import os.path
sys.path.insert(0, os.path.dirname(__file__))
import zulip_git_config as config
from . import zulip_git_config as config
VERSION = "0.9"
if config.ZULIP_API_PATH is not None:

View File

@@ -67,7 +67,7 @@ class ZulipListener extends AbstractIssueEventListener {
author, issueUrlMd, comment)
break
case ISSUE_CREATED_ID:
content = String.format("%s **created** %s priority %s, assigned to **%s**: \n\n> %s",
content = String.format("%s **created** %s priority %s, assigned to @**%s**: \n\n> %s",
author, issueUrlMd, event.issue.priorityObject.name,
assignee, title)
break

View File

@@ -9,6 +9,7 @@
#
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import sys
import six
from six.moves import input
@@ -2346,7 +2347,7 @@ class P4Sync(Command, P4UserMap):
self.labels[newestChange] = [output, revisions]
if self.verbose:
print("Label changes: %s" % self.labels.keys())
print("Label changes: %s" % (list(self.labels.keys()),))
# Import p4 labels as git tags. A direct mapping does not
# exist, so assume that if all the files are at the same revision
@@ -2779,7 +2780,7 @@ class P4Sync(Command, P4UserMap):
if short in branches:
self.p4BranchesInGit = [ short ]
else:
self.p4BranchesInGit = branches.keys()
self.p4BranchesInGit = list(branches.keys())
if len(self.p4BranchesInGit) > 1:
if not self.silent:
@@ -2921,7 +2922,7 @@ class P4Sync(Command, P4UserMap):
b = b[len(self.projectName):]
self.createdBranches.add(b)
self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60))
self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) // 60))
self.importProcess = subprocess.Popen(["git", "fast-import"],
stdin=subprocess.PIPE,
@@ -3214,7 +3215,7 @@ commands = {
def main():
if len(sys.argv[1:]) == 0:
printUsage(commands.keys())
printUsage(list(commands.keys()))
sys.exit(2)
cmdName = sys.argv[1]
@@ -3224,7 +3225,7 @@ def main():
except KeyError:
print("unknown command %s" % cmdName)
print("")
printUsage(commands.keys())
printUsage(list(commands.keys()))
sys.exit(2)
options = cmd.options

View File

@@ -23,16 +23,17 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import calendar
import errno
import hashlib
from HTMLParser import HTMLParser
from six.moves.html_parser import HTMLParser
import logging
import optparse
import os
import sys
import time
import urlparse
from six.moves import urllib
import feedparser
import zulip
@@ -87,7 +88,7 @@ def mkdir_p(path):
# Python doesn't have an analog to `mkdir -p` < Python 3.2.
try:
os.makedirs(path)
except OSError, e:
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
@@ -97,7 +98,7 @@ try:
mkdir_p(opts.data_dir)
except OSError:
# We can't write to the logfile, so just print and give up.
print >>sys.stderr, "Unable to store RSS data at %s." % (opts.data_dir,)
print("Unable to store RSS data at %s." % (opts.data_dir,), file=sys.stderr)
exit(1)
log_file = os.path.join(opts.data_dir, "rss-bot.log")
@@ -169,7 +170,7 @@ client = zulip.Client(email=opts.email, api_key=opts.api_key,
first_message = True
for feed_url in feed_urls:
feed_file = os.path.join(opts.data_dir, urlparse.urlparse(feed_url).netloc)
feed_file = os.path.join(opts.data_dir, urllib.parse.urlparse(feed_url).netloc)
try:
with open(feed_file, "r") as f:

View File

@@ -34,13 +34,13 @@ TRAC_BASE_TICKET_URL = "https://trac.example.com/ticket"
# and annoying. We solve this issue by only sending a notification
# for changes to the fields listed below.
#
# Total list of possible fields is:
# TRAC_NOTIFY_FIELDS lets you specify which fields will trigger a
# Zulip notification in response to a trac update; you should change
# this list to match your team's workflow. The complete list of
# possible fields is:
#
# (priority, milestone, cc, owner, keywords, component, severity,
# type, versions, description, resolution, summary, comment)
#
# The following is the list of fields which can be changed without
# triggering a Zulip notification; change these to match your team's
# workflow.
TRAC_NOTIFY_FIELDS = ["description", "summary", "resolution", "comment", "owner"]
## If properly installed, the Zulip API should be in your import

View File

@@ -23,10 +23,11 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import os
import sys
import optparse
import ConfigParser
import six.moves.configparser
import zulip
VERSION = "0.9"
@@ -85,14 +86,14 @@ if not options.twitter_id:
parser.error('You must specify --twitter-id')
try:
config = ConfigParser.ConfigParser()
config = six.moves.configparser.ConfigParser()
config.read(CONFIGFILE)
consumer_key = config.get('twitter', 'consumer_key')
consumer_secret = config.get('twitter', 'consumer_secret')
access_token_key = config.get('twitter', 'access_token_key')
access_token_secret = config.get('twitter', 'access_token_secret')
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
except (six.moves.configparser.NoSectionError, six.moves.configparser.NoOptionError):
parser.error("Please provide a ~/.zulip_twitterrc")
if not consumer_key or not consumer_secret or not access_token_key or not access_token_secret:
@@ -112,17 +113,17 @@ api = twitter.Api(consumer_key=consumer_key,
user = api.VerifyCredentials()
if not user.GetId():
print "Unable to log in to twitter with supplied credentials. Please double-check and try again"
print("Unable to log in to twitter with supplied credentials. Please double-check and try again")
sys.exit()
try:
since_id = config.getint('twitter', 'since_id')
except ConfigParser.NoOptionError:
except six.moves.configparser.NoOptionError:
since_id = -1
try:
user_id = config.get('twitter', 'user_id')
except ConfigParser.NoOptionError:
except six.moves.configparser.NoOptionError:
user_id = options.twitter_id
client = zulip.Client(
@@ -154,7 +155,7 @@ for status in statuses[::-1][:options.limit_tweets]:
if ret['result'] == 'error':
# If sending failed (e.g. no such stream), abort and retry next time
print "Error sending message to zulip: %s" % ret['msg']
print("Error sending message to zulip: %s" % ret['msg'])
break
else:
since_id = status.GetId()

View File

@@ -23,10 +23,11 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import os
import sys
import optparse
import ConfigParser
import six.moves.configparser
import zulip
VERSION = "0.9"
@@ -107,14 +108,14 @@ if not opts.search_terms:
parser.error('You must specify a search term.')
try:
config = ConfigParser.ConfigParser()
config = six.moves.configparser.ConfigParser()
config.read(CONFIGFILE)
consumer_key = config.get('twitter', 'consumer_key')
consumer_secret = config.get('twitter', 'consumer_secret')
access_token_key = config.get('twitter', 'access_token_key')
access_token_secret = config.get('twitter', 'access_token_secret')
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
except (six.moves.configparser.NoSectionError, six.moves.configparser.NoOptionError):
parser.error("Please provide a ~/.zulip_twitterrc")
if not (consumer_key and consumer_secret and access_token_key and access_token_secret):
@@ -122,7 +123,7 @@ if not (consumer_key and consumer_secret and access_token_key and access_token_s
try:
since_id = config.getint('search', 'since_id')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
except (six.moves.configparser.NoOptionError, six.moves.configparser.NoSectionError):
since_id = 0
try:
@@ -138,8 +139,8 @@ api = twitter.Api(consumer_key=consumer_key,
user = api.VerifyCredentials()
if not user.GetId():
print "Unable to log in to Twitter with supplied credentials.\
Please double-check and try again."
print("Unable to log in to Twitter with supplied credentials.\
Please double-check and try again.")
sys.exit()
client = zulip.Client(
@@ -182,7 +183,7 @@ for status in statuses[::-1][:opts.limit_tweets]:
if ret['result'] == 'error':
# If sending failed (e.g. no such stream), abort and retry next time
print "Error sending message to zulip: %s" % ret['msg']
print("Error sending message to zulip: %s" % ret['msg'])
break
else:
since_id = status.GetId()

View File

@@ -2,6 +2,8 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
from typing import Any, Generator, List, Tuple
import os
import sys
@@ -16,6 +18,7 @@ def version():
return version
def recur_expand(target_root, dir):
# type: (Any, Any) -> Generator[Tuple[str, List[str]], None, None]
for root, _, files in os.walk(dir):
paths = [os.path.join(root, f) for f in files]
if len(paths):
@@ -40,7 +43,7 @@ package_info = dict(
data_files=[('share/zulip/examples', ["examples/zuliprc", "examples/send-message", "examples/subscribe",
"examples/get-public-streams", "examples/unsubscribe",
"examples/list-members", "examples/list-subscriptions",
"examples/print-messages"])] + \
"examples/print-messages", "examples/recent-messages"])] + \
list(recur_expand('share/zulip', 'integrations/')),
scripts=["bin/zulip-send"],
)
@@ -48,6 +51,8 @@ package_info = dict(
setuptools_info = dict(
install_requires=['requests>=0.12.1',
'simplejson',
'six',
'typing',
],
)
@@ -65,7 +70,7 @@ except ImportError:
sys.exit(1)
try:
import requests
assert(LooseVersion(requests.__version__) >= LooseVersion('0.12.1'))
assert(LooseVersion(requests.__version__) >= LooseVersion('0.12.1')) # type: ignore # https://github.com/JukkaL/mypy/issues/1165
except (ImportError, AssertionError):
print("requests >=0.12.1 is not installed", file=sys.stderr)
sys.exit(1)

View File

@@ -22,25 +22,25 @@
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import simplejson
import requests
import time
import traceback
import urlparse
import sys
import os
import optparse
import platform
import urllib
import random
from distutils.version import LooseVersion
from six.moves.configparser import SafeConfigParser
from six.moves import urllib
import logging
import six
__version__ = "0.2.4"
__version__ = "0.2.5"
logger = logging.getLogger(__name__)
@@ -164,7 +164,7 @@ class Client(object):
config_file = get_default_config_filename()
if os.path.exists(config_file):
config = SafeConfigParser()
with file(config_file, 'r') as f:
with open(config_file, 'r') as f:
config.readfp(f, config_file)
if api_key is None:
api_key = config.get("api", "key")
@@ -245,7 +245,7 @@ class Client(object):
def do_api_query(self, orig_request, url, method="POST", longpolling = False):
request = {}
for (key, val) in orig_request.iteritems():
for (key, val) in six.iteritems(orig_request):
if not (isinstance(val, str) or isinstance(val, six.text_type)):
request[key] = simplejson.dumps(val)
else:
@@ -289,7 +289,7 @@ class Client(object):
kwargs = {kwarg: query_state["request"]}
res = requests.request(
method,
urlparse.urljoin(self.base_url, url),
urllib.parse.urljoin(self.base_url, url),
auth=requests.auth.HTTPBasicAuth(self.email,
self.api_key),
verify=self.tls_verification, timeout=90,
@@ -468,7 +468,7 @@ Client._register('list_subscriptions', method='GET', url='users/me/subscriptions
Client._register('add_subscriptions', url='users/me/subscriptions', make_request=_mk_subs)
Client._register('remove_subscriptions', method='PATCH', url='users/me/subscriptions', make_request=_mk_rm_subs)
Client._register('get_subscribers', method='GET',
computed_url=lambda request: 'streams/%s/members' % (urllib.quote(request['stream'], safe=''),),
computed_url=lambda request: 'streams/%s/members' % (urllib.parse.quote(request['stream'], safe=''),),
make_request=_kwargs_to_dict)
Client._register('render_message', method='GET', url='messages/render')
Client._register('create_user', method='POST', url='users')

View File

@@ -1,6 +1,8 @@
#!/usr/bin/env python2.7
from __future__ import absolute_import
import xml.etree.ElementTree as ET
import subprocess
from six.moves import range
# Generates the favicon images containing unread message counts.
@@ -10,7 +12,7 @@ elems = [tree.getroot().findall(
".//*[@id='%s']/{http://www.w3.org/2000/svg}tspan" % (name,))[0]
for name in ('number_back', 'number_front')]
for i in xrange(1,100):
for i in range(1, 100):
# Prepare a modified SVG
s = '%2d' % (i,)
for e in elems:

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env python2.7
from __future__ import absolute_import
from __future__ import print_function
import os
import sys
@@ -8,4 +9,4 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
os.environ['DJANGO_SETTINGS_MODULE'] = 'zproject.settings'
from django.conf import settings
print getattr(settings, sys.argv[1])
print(getattr(settings, sys.argv[1]))

View File

@@ -1,4 +1,5 @@
#!/bin/bash -e
#!/usr/bin/env bash
set -e
queue=$1

View File

@@ -1,4 +1,6 @@
#!/usr/bin/env python2.7
from __future__ import print_function
from __future__ import absolute_import
import sys
import time
import optparse
@@ -7,6 +9,7 @@ import random
import logging
import subprocess
import hashlib
from six.moves import range
parser = optparse.OptionParser()
parser.add_option('--verbose',
@@ -100,7 +103,7 @@ def print_status_and_exit(status):
# e.g. true success and punting due to a SERVNAK, result in a
# non-alert case, so to give us something unambiguous to check in
# Nagios, print the exit status.
print status
print(status)
sys.exit(status)
def send_zulip(message):
@@ -149,7 +152,7 @@ for (stream, test) in test_streams:
zephyr_subs_to_add.append((stream, '*', '*'))
actually_subscribed = False
for tries in xrange(10):
for tries in range(10):
try:
zephyr.init()
zephyr._z.subAll(zephyr_subs_to_add)
@@ -163,7 +166,7 @@ for tries in xrange(10):
if missing == 0:
actually_subscribed = True
break
except IOError, e:
except IOError as e:
if "SERVNAK received" in e:
logger.error("SERVNAK repeatedly received, punting rest of test")
else:
@@ -276,7 +279,7 @@ logger.info("Finished receiving Zulip messages!")
receive_zephyrs()
logger.info("Finished receiving Zephyr messages!")
all_keys = set(zhkeys.keys() + hzkeys.keys())
all_keys = set(list(zhkeys.keys()) + list(hzkeys.keys()))
def process_keys(content_list):
# Start by filtering out any keys that might have come from
# concurrent check-mirroring processes

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env python2.7
from __future__ import print_function
import sys
import time
import optparse
@@ -15,7 +16,7 @@ states = {
}
if 'USER' in os.environ and not os.environ['USER'] in ['root', 'rabbitmq']:
print "This script must be run as the root or rabbitmq user"
print("This script must be run as the root or rabbitmq user")
usage = """Usage: check-rabbitmq-consumers --queue=[queue-name] --min-threshold=[min-threshold]"""

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env python2.7
from __future__ import print_function
import sys
import re
import time
@@ -30,7 +31,7 @@ max_count = 0
warn_queues = []
if 'USER' in os.environ and not os.environ['USER'] in ['root', 'rabbitmq']:
print "This script must be run as the root or rabbitmq user"
print("This script must be run as the root or rabbitmq user")
for line in output.split("\n"):
line = line.strip()

View File

@@ -10,7 +10,7 @@ def nagios_from_file(results_file):
This file is created by various nagios checking cron jobs such as
check-rabbitmq-queues and check-rabbitmq-consumers"""
data = file(results_file).read().strip()
data = open(results_file).read().strip()
pieces = data.split('|')
if not len(pieces) == 4:

View File

@@ -1,9 +1,10 @@
#!/usr/bin/env python2.7
from __future__ import print_function
import sys
import time
import datetime
import optparse
import urlparse
from six.moves import urllib
import itertools
import traceback
import os
@@ -56,13 +57,13 @@ except ImportError:
parser.error('Install python-gdata')
def get_calendar_url():
parts = urlparse.urlparse(options.calendar)
parts = urllib.parse.urlparse(options.calendar)
pat = os.path.split(parts.path)
if pat[1] != 'basic':
parser.error('The --calendar URL should be the XML "Private Address" ' +
'from your calendar settings')
return urlparse.urlunparse((parts.scheme, parts.netloc, pat[0] + '/full',
'', 'futureevents=true&orderby=startdate', ''))
return urllib.parse.urlunparse((parts.scheme, parts.netloc, pat[0] + '/full',
'', 'futureevents=true&orderby=startdate', ''))
calendar_url = get_calendar_url()
@@ -99,7 +100,7 @@ def send_reminders():
key = (uid, start)
if key not in sent:
line = '%s starts at %s' % (title, start.strftime('%H:%M'))
print 'Sending reminder:', line
print('Sending reminder:', line)
messages.append(line)
keys.add(key)

View File

@@ -37,6 +37,7 @@
# | other sender| x | | |
# public mode +-------------+-----+----+--------+----
# | self sender | | | |
from typing import Set
import logging
import threading
@@ -78,11 +79,11 @@ class JabberToZulipBot(ClientXMPP):
self.nick = jid.username
jid.resource = "zulip"
ClientXMPP.__init__(self, jid, password)
self.rooms = set()
self.rooms = set() # type: Set[str]
self.rooms_to_join = rooms
self.add_event_handler("session_start", self.session_start)
self.add_event_handler("message", self.message)
self.zulip = None
self.zulip = None # type: zulip.Client
self.use_ipv6 = False
self.register_plugin('xep_0045') # Jabber chatrooms
@@ -195,7 +196,7 @@ class JabberToZulipBot(ClientXMPP):
class ZulipToJabberBot(object):
def __init__(self, zulip_client):
self.client = zulip_client
self.jabber = None
self.jabber = None # type: JabberToZulipBot
def set_jabber_client(self, client):
self.jabber = client
@@ -376,7 +377,7 @@ option does not affect login credentials.'''.replace("\n", " "))
config = SafeConfigParser()
try:
with file(config_file, 'r') as f:
with open(config_file, 'r') as f:
config.readfp(f, config_file)
except IOError:
pass

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python2.7
from __future__ import print_function
import subprocess
import os
import sys
@@ -20,7 +21,7 @@ def mkdir_p(path):
# Python doesn't have an analog to `mkdir -p` < Python 3.2.
try:
os.makedirs(path)
except OSError, e:
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
@@ -55,14 +56,14 @@ def process_logs():
data_file_path = "/var/tmp/log2zulip.state"
mkdir_p(os.path.dirname(data_file_path))
if not os.path.exists(data_file_path):
file(data_file_path, "w").write("{}")
last_data = ujson.loads(file(data_file_path).read())
open(data_file_path, "w").write("{}")
last_data = ujson.loads(open(data_file_path).read())
new_data = {}
for log_file in log_files:
file_data = last_data.get(log_file, {})
if not os.path.exists(log_file):
# If the file doesn't exist, log an error and then move on to the next file
print "Log file %s does not exist!" % (log_file,)
print("Log file does not exist or could not stat log file: %s" % (log_file,))
continue
length = int(subprocess.check_output(["wc", "-l", log_file]).split()[0])
if file_data.get("last") is None:
@@ -78,26 +79,26 @@ def process_logs():
process_lines(new_lines, filename)
file_data["last"] += len(new_lines)
new_data[log_file] = file_data
file(data_file_path, "w").write(ujson.dumps(new_data))
open(data_file_path, "w").write(ujson.dumps(new_data))
if __name__ == "__main__":
if os.path.exists(lock_path):
print "Log2zulip lock held; not doing anything"
print("Log2zulip lock held; not doing anything")
sys.exit(0)
try:
file(lock_path, "w").write("1")
open(lock_path, "w").write("1")
zulip_client = zulip.Client(config_file="/etc/log2zulip.zuliprc")
try:
log_files = ujson.loads(file(control_path, "r").read())
log_files = ujson.loads(open(control_path, "r").read())
except Exception:
print "Could not load control data from %s" % (control_path,)
print("Could not load control data from %s" % (control_path,))
traceback.print_exc()
sys.exit(1)
process_logs()
finally:
try:
os.remove(lock_path)
except OSError, IOError:
except OSError as IOError:
pass

View File

@@ -9,27 +9,27 @@ ccache_data_encoded = sys.argv[3]
# Update the Kerberos ticket cache file
program_name = "zmirror-%s" % (short_user,)
with file("/home/zulip/ccache/%s" % (program_name,), "w") as f:
with open("/home/zulip/ccache/%s" % (program_name,), "w") as f:
f.write(base64.b64decode(ccache_data_encoded))
# Setup API key
api_key_path = "/home/zulip/api-keys/%s" % (program_name,)
file(api_key_path, "w").write(api_key + "\n")
open(api_key_path, "w").write(api_key + "\n")
# Setup supervisord configuration
supervisor_path = "/etc/supervisor/conf.d/%s.conf" % (program_name,)
template = "/home/zulip/zulip/bots/zmirror_private.conf.template"
template_data = file(template).read()
template_data = open(template).read()
session_path = "/home/zulip/zephyr_sessions/%s" % (program_name,)
# Preserve mail zephyrs forwarding setting across rewriting the config file
try:
if "--forward-mail-zephyrs" in file(supervisor_path, "r").read():
if "--forward-mail-zephyrs" in open(supervisor_path, "r").read():
template_data = template_data.replace("--use-sessions", "--use-sessions --forward-mail-zephyrs")
except Exception:
pass
file(supervisor_path, "w").write(template_data.replace("USERNAME", short_user))
open(supervisor_path, "w").write(template_data.replace("USERNAME", short_user))
# Delete your session
subprocess.check_call(["rm", "-f", session_path])

View File

@@ -1,4 +1,5 @@
from __future__ import print_function
from typing import Any, Dict, List
# This is hacky code to analyze data on our support stream. The main
# reusable bits are get_recent_messages and get_words.
@@ -50,13 +51,13 @@ def generate_support_stats():
narrow = 'stream:support'
count = 2000
msgs = get_recent_messages(client, narrow, count)
msgs_by_topic = collections.defaultdict(list)
msgs_by_topic = collections.defaultdict(list) # type: Dict[str, List[Dict[str, Any]]]
for msg in msgs:
topic = msg['subject']
msgs_by_topic[topic].append(msg)
word_count = collections.defaultdict(int)
email_count = collections.defaultdict(int)
word_count = collections.defaultdict(int) # type: Dict[str, int]
email_count = collections.defaultdict(int) # type: Dict[str, int]
if False:
for topic in msgs_by_topic:
@@ -64,16 +65,14 @@ def generate_support_stats():
analyze_messages(msgs, word_count, email_count)
if True:
words = word_count.keys()
words = [w for w in words if word_count[w] >= 10]
words = [w for w in words if len(w) >= 5]
words = [w for w in word_count.keys() if word_count[w] >= 10 and len(w) >= 5]
words = sorted(words, key=lambda w: word_count[w], reverse=True)
for word in words:
print(word, word_count[word])
if False:
emails = email_count.keys()
emails = sorted(emails, key=lambda w: email_count[w], reverse=True)
emails = sorted(list(email_count.keys()),
key=lambda w: email_count[w], reverse=True)
for email in emails:
print(email, email_count[email])

View File

@@ -59,7 +59,7 @@ if __name__ == "__main__":
if public_streams is None:
continue
f = file("/home/zulip/public_streams.tmp", "w")
f = open("/home/zulip/public_streams.tmp", "w")
f.write(simplejson.dumps(list(public_streams)) + "\n")
f.close()

View File

@@ -21,6 +21,7 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from __future__ import absolute_import
from typing import Any, List
import sys
from six.moves import map
@@ -28,7 +29,7 @@ from six.moves import range
try:
import simplejson
except ImportError:
import json as simplejson
import json as simplejson # type: ignore
import re
import time
import subprocess
@@ -48,6 +49,8 @@ class States(object):
Startup, ZulipToZephyr, ZephyrToZulip, ChildSending = list(range(4))
CURRENT_STATE = States.Startup
logger = None # type: logging.Logger
def to_zulip_username(zephyr_username):
if "@" in zephyr_username:
(user, realm) = zephyr_username.split("@")
@@ -191,7 +194,7 @@ def zephyr_bulk_subscribe(subs):
def update_subscriptions():
try:
f = file(options.stream_file_path, "r")
f = open(options.stream_file_path, "r")
public_streams = simplejson.loads(f.read())
f.close()
except:
@@ -287,7 +290,7 @@ def parse_zephyr_body(zephyr_data):
def parse_crypt_table(zephyr_class, instance):
try:
crypt_table = file(os.path.join(os.environ["HOME"], ".crypt-table"))
crypt_table = open(os.path.join(os.environ["HOME"], ".crypt-table"))
except IOError:
return None
@@ -349,7 +352,7 @@ def process_notice(notice, log):
if zephyr_class == options.nagios_class:
# Mark that we got the message and proceed
with file(options.nagios_path, "w") as f:
with open(options.nagios_path, "w") as f:
f.write("0\n")
return
@@ -468,7 +471,7 @@ def zephyr_load_session_autoretry(session_path):
backoff = zulip.RandomExponentialBackoff()
while backoff.keep_going():
try:
session = file(session_path, "r").read()
session = open(session_path, "r").read()
zephyr._z.initialize()
zephyr._z.load_session(session)
zephyr.__inited = True
@@ -510,7 +513,7 @@ def zephyr_to_zulip(options):
if options.nagios_class:
zephyr_subscribe_autoretry((options.nagios_class, "*", "*"))
if options.use_sessions:
file(options.session_path, "w").write(zephyr._z.dump_session())
open(options.session_path, "w").write(zephyr._z.dump_session())
if options.logs_to_resend is not None:
with open(options.logs_to_resend, 'r') as log:
@@ -804,9 +807,9 @@ def add_zulip_subscriptions(verbose):
unauthorized = res.get("unauthorized")
if verbose:
if already is not None and len(already) > 0:
logger.info("\nAlready subscribed to: %s" % (", ".join(already.values()[0]),))
logger.info("\nAlready subscribed to: %s" % (", ".join(list(already.values())[0]),))
if new is not None and len(new) > 0:
logger.info("\nSuccessfully subscribed to: %s" % (", ".join(new.values()[0]),))
logger.info("\nSuccessfully subscribed to: %s" % (", ".join(list(new.values())[0]),))
if unauthorized is not None and len(unauthorized) > 0:
logger.info("\n" + "\n".join(textwrap.wrap("""\
The following streams you have NOT been subscribed to,
@@ -857,7 +860,7 @@ def parse_zephyr_subs(verbose=False):
logger.error("Couldn't find ~/.zephyr.subs!")
return []
for line in file(subs_file, "r").readlines():
for line in open(subs_file, "r").readlines():
line = line.strip()
if len(line) == 0:
continue
@@ -878,6 +881,7 @@ def parse_zephyr_subs(verbose=False):
return zephyr_subscriptions
def open_logger():
# type: () -> logging.Logger
if options.log_path is not None:
log_file = options.log_path
elif options.forward_class_messages:
@@ -1025,7 +1029,7 @@ if __name__ == "__main__":
signal.signal(signal.SIGINT, die_gracefully)
(options, args) = parse_args()
(options, args) = parse_args() # type: Any, List[str]
logger = open_logger()
configure_logger(logger, "parent")
@@ -1050,7 +1054,7 @@ Could not find API key file.
You need to either place your api key file at %s,
or specify the --api-key-file option.""" % (options.api_key_file,))))
sys.exit(1)
api_key = file(options.api_key_file).read().strip()
api_key = open(options.api_key_file).read().strip()
# Store the API key in the environment so that our children
# don't need to read it in
os.environ["HUMBUG_API_KEY"] = api_key
@@ -1113,7 +1117,7 @@ or specify the --api-key-file option.""" % (options.api_key_file,))))
options.session_path = "/var/tmp/%s" % (options.user,)
if options.forward_from_zulip:
child_pid = os.fork()
child_pid = os.fork() # type: int
if child_pid == 0:
CURRENT_STATE = States.ZulipToZephyr
# Run the zulip => zephyr mirror in the child

View File

@@ -4,6 +4,59 @@ All notable changes to this project will be documented in this file.
[Unreleased]
[1.3.11]
- Moved email digest support into the default Zulip production configuration.
- Added options for configuring Postgres, RabbitMQ, Redis, and memcached
in settings.py.
- Added documentation on using Hubot to integrate with useful services
not yet integrated with Zulip directly (e.g. Google Hangouts).
- Added new management command to test sending email from Zulip.
- Added Codeship, Pingdom, Taiga, Teamcity, and Yo integrations.
- Added Nagios plugins to the main distribution.
- Added ability for realm administrators to manage custom emoji.
- Added guide to writing new integrations.
- Enabled camo image proxy to fix mixed-content warnings for http images.
- Refactored the Zulip puppet modules to be more modular.
- Refactored the Tornado event system, fixing old memory leaks.
- Removed many old-style /json API endpoints
- Implemented running queue processors multithreaded in development,
decreasing RAM requirements for a Zulip development environment from
~1GB to ~300MB.
- Fixed rerendering the complete buddy list whenever a user came back from
idle, which was a significant performance issue in larger realms.
- Fixed the disabling of desktop notifications from 1.3.7 for new users.
- Fixed the (admin) create_user API enforcing restricted_to_domain, even
if that setting was disabled for the realm.
- Fixed bugs changing certain settings in administration pages.
- Fixed collapsing messages in narrowed views.
- Fixed 500 errors when uploading a non-image file as an avatar.
- Fixed Jira integration incorrectly not @-mentioning assignee.
[1.3.10]
- Added new integration for Travis CI.
- Added settings option to control maximum file upload size.
- Added support for running Zulip development environment in Docker.
- Added easy configuration support for a remote postgres database.
- Added extensive documentation on scalability, backups, and security.
- Recent private message threads are now displayed expanded similar to
the pre-existing recent topics feature.
- Made it possible to set LDAP and EMAIL_HOST passwords in
/etc/zulip/secrets.conf.
- Improved the styling for the Administration page and added tabs.
- Substantially improved loading performance on slow networks by enabling
GZIP compression on more assets.
- Changed the page title in narrowed views to include the current narrow.
- Fixed several backend performance issues affecting very large realms.
- Fixed bugs where draft compose content might be lost when reloading site.
- Fixed support for disabling the "zulip" notifications stream.
- Fixed missing step in postfix_localmail installation instructions.
- Fixed several bugs/inconveniences in the production upgrade process.
- Fixed realm restrictions for servers with a unique, open realm.
- Substantially cleaned up console logging from run-dev.py.
[1.3.9] - 2015-11-16
- Fixed buggy #! lines in upgrade scripts.
[1.3.8] - 2015-11-15
- Added options to the Python api for working with untrusted server certificates.
- Added a lot of documentation on the development environment and testing.

View File

@@ -2,9 +2,10 @@
# Copyright: (c) 2008, Jarek Zgoda <jarek.zgoda@gmail.com>
from typing import Any, Dict
__revision__ = '$Id: settings.py 12 2008-11-23 19:38:52Z jarek.zgoda $'
STATUS_ACTIVE = 1
STATUS_FIELDS = {
}
STATUS_FIELDS = {} # type: Dict[Any, Any]

View File

@@ -6,4 +6,10 @@ urlpatterns = patterns('',
url(r'^zephyr/$', TemplateView.as_view(template_name='corporate/zephyr.html')),
url(r'^mit/$', TemplateView.as_view(template_name='corporate/mit.html')),
url(r'^zephyr-mirror/$', TemplateView.as_view(template_name='corporate/zephyr-mirror.html')),
# Terms of service and privacy policy
url(r'^terms/$', TemplateView.as_view(template_name='corporate/terms.html')),
url(r'^terms-enterprise/$', TemplateView.as_view(template_name='corporate/terms-enterprise.html')),
url(r'^privacy/$', TemplateView.as_view(template_name='corporate/privacy.html')),
)

View File

@@ -79,13 +79,13 @@ In our Django code, never do direct
use ``get_user_profile_by_{email,id}``. There are 3 reasons for this:
#. It's guaranteed to correctly do a case-inexact lookup
#. It fetches the user object from memcached, which is faster
#. It fetches the user object from remote cache, which is faster
#. It always fetches a UserProfile object which has been queried using
.selected\_related(), and thus will perform well when one later
accesses related models like the Realm.
Similarly we have ``get_client`` and ``get_stream`` functions to fetch
those commonly accessed objects via memcached.
those commonly accessed objects via remote cache.
Using Django model objects as keys in sets/dicts
------------------------------------------------
@@ -391,7 +391,11 @@ Commit Discipline
-----------------
We follow the Git project's own commit discipline practice of "Each
commit is a minimal coherent idea".
commit is a minimal coherent idea". This discipline takes a bit of
work, but it makes it much easier for code reviewers to spot bugs, and
makesthe commit history a much more useful resource for developers
trying to understand why the code works the way it does, which also
helps a lot in preventing bugs.
Coherency requirements for any commit:
@@ -436,12 +440,28 @@ Other considerations:
- Overly fine commits are easily squashed, but not vice versa, so err
toward small commits, and the code reviewer can advise on squashing.
- If a commit you write doesn't pass tests, you should usually fix
that by amending the commit to fix the bug, not writing a new "fix
tests" commit on top of it.
- When you fix a GitHub issue, `mark that you've fixed the issue in
your commit message
<https://help.github.com/articles/closing-issues-via-commit-messages/>`__
so that the issue is automatically closed when your code is merged.
Zulip's preferred style for this is to have the final paragraph
of the commit message read e.g. "Fixes: #123."
It can take some practice to get used to writing your commits this
way. For example, often you'll start adding a feature, and discover
you need to a refactoring partway through writing the feature. When
that happens, we recommend stashing your partial feature, do the
refactoring, commit it, and then finish implementing your feature.
Zulip expects you to structure the commits in your pull requests to
form a clean history before we will merge them; it's best to write
your commits following these guidelines in the first place, but if you
don't, you can always fix your history using `git rebase -i`.
It can take some practice to get used to writing your commits with a
clean history so that you don't spend much time doing interactive
rebases. For example, often you'll start adding a feature, and
discover you need to a refactoring partway through writing the
feature. When that happens, we recommend stashing your partial
feature, do the refactoring, commit it, and then finish implementing
your feature.
Commit Messages
---------------
@@ -454,18 +474,35 @@ Bad::
bugfix
gather_subscriptions was broken
fix bug #234.
Good::
Prevent gather_subscriptions from throwing an exception when given bad input.
Fix gather_subscriptions throwing an exception when given bad input.
- Please use a complete sentence, ending with a period.
- Use present-tense action verbs in your commit messages.
Bad::
Fixing gather_subscriptions throwing an exception when given bad input.
Fixed gather_subscriptions throwing an exception when given bad input.
Good::
Fix gather_subscriptions throwing an exception when given bad input.
- Please use a complete sentence in the summary, ending with a
period.
- The rest of the commit message should be written in full prose and
explain why and how the change was made. If the commit makes
performance improvements, you should generally include some rough
benchmarks showing that it actually improves the performance.
- Any paragraph content in the commit message should be line-wrapped
to less than 76 characters per line, so that your commit message
will be reasonably readable in `git log` in a normal terminal.
- In your commit message, you should describe any manual testing you
did in addition to running the automated tests, and any aspects of
the commit that you think are questionable and you'd like special

View File

@@ -293,3 +293,11 @@ texinfo_documents = [
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
from recommonmark.parser import CommonMarkParser
source_parsers = {
'.md': CommonMarkParser,
}
source_suffix = ['.rst', '.md']

View File

@@ -75,13 +75,13 @@ Templates
Tests
=====
+------------------------+-----------------------------------+
| ``zerver/test*.py`` | Backend tests |
+------------------------+-----------------------------------+
| ``frontend_tests/node`` | Node Frontend unit tests |
+------------------------+-----------------------------------+
| ``frontend_tests/tests`` | Casper frontend tests |
+------------------------+-----------------------------------+
+-------------------------+-----------------------------------+
| ``zerver/tests/`` | Backend tests |
+-------------------------+-----------------------------------+
| ``frontend_tests/node`` | Node Frontend unit tests |
+-------------------------+-----------------------------------+
| ``frontend_tests/tests``| Casper frontend tests |
+-------------------------+-----------------------------------+
Documentation
=============

View File

@@ -0,0 +1,71 @@
=======================
Front End Build Process
=======================
This page documents additional information that may be useful when developing new features for Zulip that require front-end changes. For a more general overview, see the new feature tutorial. The code style documentation also has relevant information about how Zulip's code is structured.
Primary build process
=====================
Most of the existing JS in Zulip is written in IIFE-wrapped modules,
one per file in the `static/js` directory. When running Zulip in
development mode, each file is loaded seperately. In production mode
(and when creating a release tarball using
`tools/build-release-tarball`), JavaScript files are concatenated and
minified.
If you add a new JavaScript file, it needs to be specified in the
`JS_SPECS` dictionary defined in `zproject/settings.py` to be included
in the concatenated file.
Webpack/CommonJS modules
========================
New JS written for Zulip can be written as CommonJS modules (bundled
using `webpack <https://webpack.github.io/>`_, though this will taken
care of automatically whenever ``run-dev.py`` is running). (CommonJS
is the same module format that Node uses, so see `the Node
documentation <https://nodejs.org/docs/latest/api/modules.html>` for
more information on the syntax.)
Benefits of using CommonJS modules over the `IIFE
<http://benalman.com/news/2010/11/immediately-invoked-function-expression/>`_
module approach:
* namespacing/module boilerplate will be added automatically in the bundling process
* dependencies between modules are more explicit and easier to trace
* no separate list of JS files needs to be maintained for concatenation and minification
* third-party libraries can be more easily installed/versioned using npm
* running the same code in the browser and in Node for testing is
simplified (as both environments use the same module syntax)
The entry point file for the bundle generated by webpack is
``static/js/src/main.js``. Any modules you add will need to be
required from this file (or one of its dependencies) in order to be
included in the script bundle.
Adding static files
===================
To add a static file to the app (JavaScript, CSS, images, etc), first
add it to the appropriate place under ``static/``.
* Third-party files should all go in ``static/third/``. Tag the commit
with "[third]" when adding or modifying a third-party package.
* Our own JS lives under ``static/js``; CSS lives under ``static/styles``.
* JavaScript and CSS files are combined and minified in production. In
this case all you need to do is add the filename to PIPELINE_CSS or
JS_SPECS in ``zproject/settings.py``. (If you plan to only use the
JS/CSS within the app proper, and not on the login page or other
standalone pages, put it in the 'app' category.)
If you want to test minified files in development, look for the
``PIPELINE =`` line in ``zproject/settings.py`` and set it to ``True`` -- or
just set ``DEBUG = False``.
Note that ``static/html/{400,5xx}.html`` will only render properly if
minification is enabled, since they hardcode the path
``static/min/portico.css``.

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python2.7
from __future__ import print_function
# Remove HTML entity escaping left over from MediaWiki->rST conversion.

View File

@@ -11,10 +11,18 @@ Contents:
.. toctree::
:maxdepth: 2
integration-guide
new-feature-tutorial
code-style
directory-structure
code-style
testing
markdown
queuing
schema-migrations
front-end-build-process
mypy
translating
roadmap
Indices and tables
==================

178
docs/integration-guide.md Normal file
View File

@@ -0,0 +1,178 @@
# Integration Writing Guide
Integrations are one of the most important parts of a group chat tool
like Zulip, and we are committed to making integrating with Zulip and
getting you integration merged upstream so everyone else can benefit
from it as easy as possible while maintaining the high quality of the
Zulip integrations library.
Contributions to this guide are very welcome, so if you run into any
issues following these instructions or come up with any tips or tools
that help writing integration, please email
zulip-devel@googlegroups.com, open an issue, or submit a pull request
to share your ideas!
## Types of integrations
We have several different ways that we integrate with 3rd part
products, ordered here by which types we prefer to write:
1. Webhook integrations (examples: Freshdesk, GitHub), where the
third-party service supports posting content to a particular URI on
our site with data about the event. For these, you usually just need
to add a new handler in `zerver/views/webhooks.py` (plus
test/document/etc.). An example commit implementing a new webhook.
https://github.com/zulip/zulip/pull/324.
2. Python script integrations (examples: SVN, Git), where we can get
the service to call our integration (by shelling out or otherwise),
passing in the required data. Our preferred model for these is to
ship these integrations in our API release tarballs (by writing the
integration in `api/integrations`).
3. Plugin integrations (examples: Jenkins, Hubot, Trac) where the user
needs to install a plugin into their existing software. These are
often more work, but for some products are the only way to integrate
with the product at all.
## General advice for writing integrations
* Consider using our Zulip markup to make the output from your
integration especially attractive or useful (e.g. emoji, markdown
emphasis, @-mentions, or `!avatar(email)`).
* Use topics effectively to ensure sequential messages about the same
thing are threaded together; this makes for much better consumption
by users. E.g. for a bug tracker integration, put the bug number in
the topic for all messages; for an integration like Nagios, put the
service in the topic.
* Integrations that don't match a team's workflow can often be
uselessly spammy. Give careful thought to providing options for
triggering Zulip messages only for certain message types, certain
projects, or sending different messages to different streams/topics,
to make it easy for teams to configure the integration to support
their workflow.
* Sometimes it can be helpful to contact the vendor if it appears they
don't have an API or webhook we can use -- sometimes the right API
is just not properly documented.
## Writing Webhook integrations
New Zulip webhook integrations can take just a few hours to write,
including tests and documentation, if you use the right process.
Here's how we recommend doing it:
* First, use http://requestb.in/ or a similar site to capture an
example webhook payload from the service you're integrating. You
can use these captured payloads to create a set of test fixtures for
your integration under `zerver/fixtures`.
* Then write a draft webhook handler under `zerver/views/webhooks/`;
there are a lot of examples in that directory. We recommend
templating off a short one (like `stash.py` or `zendesk.py`), since
the longer ones usually just have more complex parsing which can
obscure what's common to all webhook integrations. In addition to
writing the integration itself, you'll need to add an entry in
`zproject/urls.py` for your webhook; search for `webhook` in that
file to find the existing ones (and please add yours in the
alphabetically correct place).
* Then write a test for your fixture in `zerver/tests/test_hooks.py`, and
you can iterate on the tests and webhooks handler until they work,
all without ever needing to post directly from the server you're
integrating to your Zulip development machine. To run just the
tests from the test class you wrote, you can use e.g.
```
test-backend zerver.tests.test_hooks.PagerDutyHookTests
```
See
https://github.com/zulip/zulip/blob/master/README.dev.md#running-the-test-suite
for more details on the Zulip test runner.
* Once you've gotten your webhook working and passing a test, capture
payloads for the other common types of posts the service's webhook
will make, and add tests for them; usually this part of the process
is pretty fast. Webhook integration tests should all use fixtures
(as opposed to contacting the service), since otherwise the tests
can't run without Internet access and some sort of credentials for
the service.
* Finally, write documentation for the integration (see below)!
## Writing Python script and plugin integrations integrations
For plugin integrations, usually you will need to consult the
documentation for the third party software in order to learn how to
write the integration. But we have a few notes on how to do these:
* You should always send messages by POSTing to URLs of the form
`https://zulip.example.com/v1/messages/`, not the legacy
`/api/v1/send_message` message sending API.
* We usually build Python script integration with (at least) 2 files:
`zulip_foo_config.py`` containing the configuration for the
integration including the bots' API keys, plus a script that reads
from this configuration to actually do the work (that way, it's
possible to update the script without breaking users' configurations).
* Be sure to test your integration carefully and document how to
install it (see notes on documentation below).
* You should specify a clear HTTP User-Agent for your integration. The
user agent should at a minimum identify the integration and version
number, separated by a slash. If possible, you should collect platform
information and include that in `()`s after the version number. Some
examples of ideal UAs are:
```
ZulipDesktop/0.7.0 (Ubuntu; 14.04)
ZulipJenkins/0.1.0 (Windows; 7.2)
ZulipMobile/0.5.4 (Android; 4.2; maguro)
```
## Documenting your integration
Every Zulip integration must be documented in
`templates/zerver/integrations.html`. Usually, this involves a few
steps:
* Add an `integration-lozenge` class block in the alphabetically
correct place in the main integration list, using the logo for the
integrated software.
* Add an `integration-instructions` class block also in the
alphabetically correct place, explaining all the steps required to
setup the integration, including what URLs to use, etc. If there
are any screens in the product involved, take a few screenshots with
the input fields filled out with sample values in order to make the
instructions really easy to follow. For the screenshots, use
something like `github-bot@example.com` for the email addresses and
an obviously fake API key like `abcdef123456790`.
* Finally, generate a message sent by the integration and take a
screenshot of the message to provide an example message in the
documentation. If your new integration is a webhook integration,
you can generate such a message from your test fixtures
using `send_webhook_fixture_message`:
```
./manage.py send_webhook_fixture_message \
--fixture=zerver/fixtures/pingdom/pingdom_imap_down_to_up.json \
'--url=/api/v1/external/pingdom?stream=stream_name&api_key=api_key'
```
When generating the screenshot of a sample message, give your test
bot a nice name like "GitHub Bot", use the project's logo as the
bot's avatar, and take the screenshots showing the stream/topic bar
for the message, not just the message body.
When writing documentation for your integration, be sure to use the
`{{ external_api_uri }}` template variable, so that your integration
documentation will provide the correct URL for whatever server it is
deployed on. If special configuration is required to set the SITE
variable, you should document that too, inside an `{% if
api_site_required %}` check.

135
docs/markdown.md Normal file
View File

@@ -0,0 +1,135 @@
# Zulip's markdown implementation
Zulip has a special flavor of Markdown, currently called 'bugdown'
after Zulip's original name of "humbug".
Zulip has two implementations of Bugdown. The first is based on
Python-Markdown (`zerver/lib/bugdown/`) and is used to authoritatively
render messages on the backend (and implements expensive features like
querying the Twitter API to render tweets nicely). The other is in
javascript, based on marked (`static/js/echo.js`), and is used to
preview and locally echo messages the moment the sender hits enter,
without waiting for round trip from the server. The two
implementations are tested for compatibility via
`zerver/tests/test_bugdown.py` and the fixtures under
`zerver/fixtures/bugdown-data.json`.
The javascript implementation knows which types of messages it can
render correctly, and thus while there is code to rerender messages
based on the authoritative backend rendering (which would clause a
change in the rendering visible only to the sender shortly after a
message is sent), this should never happen and whenever it does it is
considered a bug. Instead, if the frontend doesn't know how to
correctly render a message, we simply won't echo the message for the
sender until it's rendered by the backend. So for example, a message
containing a link to Twitter will not be rendered by the javascript
implementation because it doesn't support doing the 3rd party API
queries required to render tweets nicely.
I should note that the below documentation is based on a comparison
with original Markdown, not newer Markdown variants like CommonMark.
## Zulip's Markdown philosophy
Markdown is great for group chat for the same reason it's been
successful in products ranging from blogs to wikis to bug trackers:
it's close enough to how people try to express themselves when writing
plain text (e.g. emails) that is helps more than getting in the way.
The main issue for using Markdown in instant messaging is that the
Markdown standard syntax used in a lot of wikis/blogs has nontrivial
error rates, where the author needs to go back and edit the post to
fix the formatting after typing it the first time. While that's
basically fine when writing a blog, it gets annoying very fast in a
chat product; even though you can edit messages to fix formatting
mistakes, you don't want to be doing that often. There are basically
2 types of error rates that are important for a product like Zulip:
* What fraction of the time, if you pasted a short technical email
that you wrote to your team and passed it through your Markdown
implementation, would you need to change the text of your email for it
to render in a reasonable way? This is the "accidental Markdown
syntax" problem, common with Markdown syntax like the italics syntax
interacting with talking about `char *`s.
* What fraction of the time do users attempting to use a particular
Markdown syntax actually succeed at doing so correctly? Syntax like
required a blank line between text and the start of a bulleted list
raise this figure substantially.
Both of these are minor issues for most products using Markdown, but
they are major problems in the instant messaging context, because one
can't edit a message that has already been sent and users are
generally writing quickly. Zulip's Markdown strategy is based on the
principles of giving users the power they need to express complicated
ideas in a chat context while minimizing those two error rates.
## Zulip's Changes to Markdown
Below, we document the changes that Zulip has against stock
Python-Markdown; some of the features we modify / disable may already
be non-standard.
### Basic syntax
* Enable `nl2br</tt> extension: this means one newline creates a line
break (not paragraph break).
* Disable italics entirely. This resolves an issue where people were
using `*` and `_` and hitting it by mistake too often. E.g. with
stock Markdown `You should use char * instead of void * there` would
trigger italics.
* Allow only `**` syntax for bold, not `__` (easy to hit by mistake if
discussing Python `__init__` or something)
* Disable special use of `\` to escape other syntax. Rendering `\\` as
`\` was hugely controversial, but having no escape syntax is also
controversial. We may revisit this. For now you can always put
things in code blocks.
### Lists
* Allow tacking a bulleted list or block quote onto the end of a
paragraph, i.e. without a blank line before it
* Allow only `*` for bulleted lists, not `+` or `-` (previoulsy
created confusion with diff-style text sloppily not included in a
code block)
* Disable ordered list syntax: it automatically renumbers, which can
be really confusing when sending a numbered list across multiple
messages.
### Links
* Enable auto-linkification, both for `http://...` and guessing at
things like `t.co/foo`.
* Force links to be absolute. `[foo](google.com)` will go to
`http://google.com`, and not `http://zulip.com/google.com` which
is the default behavior.
* Set `target="_blank"` and `title=`(the url) on every link tag so
clicking always opens a new window
* Disable link-by-reference syntax, `[foo][bar]` ... `[bar]: http://google.com`
### Code
* Enable fenced code block extension, with syntax highlighting
* Disable line-numbering within fenced code blocks -- the `<table>`
output confused our web client code.
### Other
* Disable headings, both `# foo` and `== foo ==` syntax: they don't
make much sense for chat messages.
* Disabled images.
* Allow embedding any avatar as a tiny (list bullet size) image. This
is used primarily by version control integrations.
* We added the `~~~ quote` block quote syntax.

78
docs/mypy.md Normal file
View File

@@ -0,0 +1,78 @@
# mypy Python static type checker
[mypy](http://mypy-lang.org/) is a compile-time static type checker
for Python, allowing optional, gradual typing of Python code. Zulip
is using mypy's Python 2 compatible syntax for type annotations, which
means that type annotations are written inside comments that start
with `# type: `. Here's a brief example of the mypy syntax we're
using in Zulip:
```
user_dict = {} # type: Dict[str, UserProfile]
def get_user_profile_by_email(email):
# type: (str) -> UserProfile
... # Actual code of the function here
```
You can learn more about it at:
* [Python 2 type annotation syntax in PEP 484](https://www.python.org/dev/peps/pep-0484/#suggested-syntax-for-python-2-7-and-straddling-code)
* [Using mypy with Python 2 code](http://mypy.readthedocs.io/en/latest/python2.html)
The mypy type checker is run automatically as part of Zulip's Travis
CI testing process.
## Installing mypy
If you installed Zulip's development environment correctly, mypy
should already be installed inside the Python 3 virtualenv at
`zulip-py3-venv` (mypy only supports Python 3). If it isn't installed
(e.g. because you haven't reprovisioned recently), you can run
`tools/install-mypy` to install it.
## Running mypy on Zulip's code locally
To run mypy on Zulip's python code, run the command:
tools/run-mypy
It will output errors in the same style of a compiler. For example,
if your code has a type error like this:
```
foo = 1
foo = '1'
```
you'll get an error like this:
```
test.py: note: In function "test":
test.py:200: error: Incompatible types in assignment (expression has type "str", variable has type "int")
```
If you need help interpreting or debugging mypy errors, please feel
free to mention @sharmaeklavya2 or @timabbott on your pull request (or
email zulip-devel@googlegroups.com) to get help; we'd love to both
build a great troubleshooting guide in this doc and also help
contribute improvements to error messages upstream.
Since mypy is a new tool under rapid development and occasionally
makes breaking changes, Zulip is using a pinned version of mypy from
its [git repository](https://github.com/python/mypy) rather than
tracking the (older) latest mypy release on pypi.
## Excluded files
Since several python files in Zulip's code don't pass mypy's checks
(even for unannotated code) right now, a list of files to be excluded
from the check for CI is present in tools/run-mypy.
To run mypy on all python files, ignoring the exclude list, you can
pass the `--all` option to tools/run-mypy.
tools/run-mypy --all
If you type annotate some of those files, please remove them from the
exclude list.

View File

@@ -62,8 +62,8 @@ process.
**Testing:** There are two types of frontend tests: node-based unit tests and
blackbox end-to-end tests. The blackbox tests are run in a headless browser
using Casper.js and are located in ``zerver/tests/frontend/tests/``. The unit
tests use Node's ``assert`` module are located in ``zerver/tests/frontend/node/``.
using Casper.js and are located in ``frontend_tests/casper_tests/``. The unit
tests use Node's ``assert`` module are located in ``frontend_tests/node_tests/``.
For more information on writing and running tests see the :doc:`testing
documentation <testing>`.

81
docs/queuing.md Normal file
View File

@@ -0,0 +1,81 @@
# RabbitMQ queues
Zulip uses RabbitMQ to manage a system of internal queues. These are
used for a variety of purposes:
* Asynchronously doing expensive operations like sending email
notifications which can take seconds per email and thus would
otherwise timeout when 100s are triggered at once (E.g. inviting a
lot of new users to a realm).
* Asynchronously doing non-time-critical somewhat expensive operations
like updating analytics tables (e.g. UserActivityInternal) which
don't have any immediate runtime effect.
* Communicating events to push to clients (browsers, etc.) from the
main Zulip Django application process to the Tornado-based events
system. Example events might be that a new message was sent, a user
has changed their subscriptions, etc.
* Processing mobile push notifications and email mirroring system
messages.
* Processing various errors, frontend tracebacks, and slow database
queries in a batched fashion.
* Doing markdown rendering for messages delivered to the Tornado via
websockets.
Needless to say, the RabbitMQ-based queuing system is an important
part of the overall Zulip architecture, since it's in critical code
paths for everything from signing up for account, to rendering
messages, to delivering updates to clients.
We use the `pika` library to interface with RabbitMQ, using a simple
custom integration defined in `zerver/lib/queue.py`.
### Adding a new queue processor
To add a new queue processor:
* Define the processor in `zerver/worker/queue_processors.py` using
the `@assign_queue` decorator; it's pretty easy to get the template
for an existing similar queue processor. This suffices to test your
queue worker in the Zulip development environment, though you'll
need to restart `tools/run-dev.py` in order to run your new queue
processor. You can also run a single queue processor manually using
e.g. `./manage.py process_queue --queue=user_activity`.
* So that supervisord will known to run the queue processor in
production, you will need to define a program entry for it in
`servers/puppet/modules/zulip/files/supervisor/conf.d/zulip.conf`
and add it to the `zulip-workers` group further down in the file.
* For monitoring, you need to add a check that your worker is running
to puppet/zulip/files/cron.d/rabbitmq-numconsumers if it's a
one-at-a-time consumer like `user_activity_internal` or a custom
nagios check if it is a bulk processor like `slow_queries`.
### Publishing events into a queue
You can publish events to a RabbitMQ queue using the
`queue_json_publish` function defined in `zerver/lib/queue.py`.
### Clearing a RabbitMQ queue
If you need to clear a queue (delete all the events in it), run
`./manage.py purge_queue <queue_name>`, for example:
```
./manage.py purge_queue user_activity
```
You can also use the amqp tools directly. Install `amqp-tools` from
apt and then run:
```
amqp-delete-queue --username=zulip --password='...' --server=localhost \
--queue=user_presence
```
with the RabbitMQ password from `/etc/zulip/zulip-secrets.conf`.

262
docs/roadmap.md Normal file
View File

@@ -0,0 +1,262 @@
Zulip 2016 Roadmap
==================
## Introduction
Zulip has received a great deal of interest and attention since it was
released as free and open source software by Dropbox. That attention
has come with a lot of active development work from members of the
Zulip community. From when Zulip was released as open source in late
September 2015 through today (mid-April, 2016), over 300 pull requests
have been submitted to the various Zulip repositories (and over 250
have been merged!), the vast majority of which are submitted by
Zulip's users around the world (as opposed to the small core team who
review and merge the pull requests).
In any project, there can be a lot of value in periodically putting
together a roadmap detailing the major areas where the project is
hoping to improve. This can be especially important in an open source
project like Zulip where development is distributed across many people
around the world. This roadmap is intended to organize a list of the
most important improvements that should to be made to Zulip in the
relatively near future. Our aim is to complete most of these
improvements in 2016.
This document is not meant to constrain in any way what contributions
to Zulip will be accepted; instead, it will be used by the Zulip core
team to prioritize our efforts, measure progress on improving the
Zulip product, hold ourselves accountable for making Zulip improve
rapidly, and celebrate members of the community who contribute to
projects on the roadmap.
If you're someone interested in making a larger contribution to Zulip
and looking for somewhere to start, this roadmap is the best place to
look for substantial projects that will definitely be of value to the
community (if you're looking for a starter project, see the [guide to
getting involved with
Zulip](https://github.com/zulip/zulip#how-to-get-involved-with-contributing-to-zulip)).
Without further ado, below is the Zulip 2016 roadmap.
## Burning problems
The top problem for the Zulip project is the state of the mobile apps.
The Android app has started seeing rapid progress thanks to a series
of contributions by Lisa Neigut of Recurse Center, and we believe to
be on a good path. The iOS app has fewer features than Android and
has more bugs, but more importantly is in need of an experienced iOS
developer who has time to drive the project.
## Core User Experience
This category includes important improvements to the core user
experience that will benefit all users.
* [Improve missed message notifications to make "reply" work nicely](https://github.com/zulip/zulip/issues/612)
* [Add support for showing "user is typing" notifications](https://github.com/zulip/zulip/issues/150)
* [Add pretty bubbles for recipients in the compose box](https://github.com/zulip/zulip/issues/595)
* [Finish and merge support for pinning a few important streams](https://github.com/zulip/zulip/issues/285)
* [Display stream descriptions more prominently](https://github.com/zulip/zulip/issues/164)
* [Integration inline URL previews](https://github.com/zulip/zulip/issues/406)
* [Add support for managing uploaded files](https://github.com/zulip/zulip/issues/454)
* [Make Zulip onboarding experience smoother for teams not used to topics](https://github.com/zulip/zulip/issues/647). That specific proposal might not be right but the issue is worth investing time in.
## Ease of setup and onboarding issues
This category focuses on issues users experience when installing a new
Zulip server or setting up a new Zulip realm.
* [Create a web flow for setting up a new realm / the first realm on a new server (currently, it's a command-line process)](https://github.com/zulip/zulip/issues/260)
* [Document or better script solution to rabbitmq startup issues](https://github.com/zulip/zulip/issues/465)
* [Add a mechanism for deleting early test messages](https://github.com/zulip/zulip/issues/135)
* [Merge a supported way to use Zulip in Docker in production
implementation](https://github.com/zulip/zulip/pull/450).
## Internationalization
The core Zulip UI has been mostly translated into 5 languages;
however, more work is required to make those translations actually
displayed in the Zulip UI for the users who would benefit from them.
* [Merge support for using translations in Django templates](https://github.com/zulip/zulip/pull/607)
* [Add text in handlebars templates to translatable string database](https://github.com/zulip/zulip/issues/726)
* [Merge support for translating text in handlebars](https://github.com/zulip/zulip/issues/726)
* [Add text in error messages to translatable strings](https://github.com/zulip/zulip/issues/727)
## User Experience at scale
There are a few parts of the Zulip UI which could benefit from
overhauls designed around making the user experience nice for large
teams.
* [Make the buddy list work better for large teams](https://github.com/zulip/zulip/issues/236)
* [Improve @-mentioning syntax based on stronger unique identifiers](https://github.com/zulip/zulip/issues/374)
* [Show subscriber counts on streams](https://github.com/zulip/zulip/pull/525)
* [Make the streams page easier to navigate with 100s of streams](https://github.com/zulip/zulip/issues/563)
* [Add support for filtering long lists of streams](https://github.com/zulip/zulip/issues/565)
## Administration and management
Currently, Zulip has a number of administration features that can be
controlled only via the command line.
* [Make default streams web-configurable](https://github.com/zulip/zulip/issues/665)
* [Make realm emoji web-configurable](https://github.com/zulip/zulip/pull/543)
* [Make realm filters web-configurable](https://github.com/zulip/zulip/pull/544)
* [Make realm aliases web-configurable](https://github.com/zulip/zulip/pull/651)
* [Enhance the LDAP integration and make it web-configurable](https://github.com/zulip/zulip/issues/715)
* [Add a SAML integration for Zulip](https://github.com/zulip/zulip/issues/716)
* [Improve administrative controls for managing streams](https://github.com/zulip/zulip/issues/425)
## Scalability
Zulip should support 10000 users in a realm and also support smaller
realms in more resource-constrained environments (probably a good
initial goal is working well with only 2GB of RAM).
* [Make the Zulip Tornado service support horizontal scaling](https://github.com/zulip/zulip/issues/445)
* [Make presence system scale well to 10000 users in a realm.](https://github.com/zulip/zulip/issues/728)
* [Support running queue workers multithreaded in production to
decrease minimum memory footprint](https://github.com/zulip/zulip/issues/34)
## Performance
Performance is essential for a communication tool. While some things
are already quite good (E.g. narrowing and message sending is speedy),
this is an area where one can always improve. There are a few known
performance opportunities:
* [Migrate to faster jinja2 templating engine](https://github.com/zulip/zulip/issues/620)
* [Don't load zxcvbn when it isn't needed](https://github.com/zulip/zulip/issues/263)
* [Optimize the frontend performance of loading the Zulip webapp using profiling](https://github.com/zulip/zulip/issues/714)
## Technology improvements
Zulip should be making use of the best Python/Django tools available.
* [Add support for Zulip running on Python 3](https://github.com/zulip/zulip/issues/256)
* [Add support for changing users' email addresses](https://github.com/zulip/zulip/issues/734)
* [Automatic thumbnailing of uploaded images](https://github.com/zulip/zulip/issues/432)
* [Upgrade Zulip to use Django 1.10 once it is released. The patches
needed to run Zulip were merged into mainline Django in Django 1.10,
so this will mean we don't need to use a fork of Django anymore.](https://github.com/zulip/zulip/issues/3)
## Technical Debt
While the Zulip server has a great codebase compared to most projects
of its size, it takes work to keep it that way.
* [Migrate most web routes to REST API](https://github.com/zulip/zulip/issues/611)
* [Finish purging global variables from the Zulip javascript](https://github.com/zulip/zulip/issues/610)
* [Finish deprecating and remove the pre-REST Zulip /send_message API](https://github.com/zulip/zulip/issues/730)
* [Split Tornado subsystem into a separate Django app](https://github.com/zulip/zulip/issues/729)
* [Clean up clutter in the root of the zulip.git repository](https://github.com/zulip/zulip/issues/707)
* [Refactor zulip.css to be broken into components](https://github.com/zulip/zulip/issues/731)
## Deployment and upgrade process
* [Support backwards-incompatible upgrades to Python libraries](https://github.com/zulip/zulip/issues/717)
* [Minimize the downtime required in Zulip upgrade process](https://github.com/zulip/zulip/issues/646)
## Security
* [Add support for 2-factor authentication on all platforms](https://github.com/zulip/zulip/pull/451)
* [Add a retention policy feature that automatically deletes old messages](https://github.com/zulip/zulip/issues/106)
* [Upgrade every Zulip dependency to a modern version](https://github.com/zulip/zulip/issues/717)
* [The LOCAL_UPLOADS_DIR file uploads backend only supports world-readable uploads](https://github.com/zulip/zulip/issues/320)
* [Add support for stronger security controls for uploaded files](https://github.com/zulip/zulip/issues/320)
## Testing
* [Extend Zulip's automated test coverage to include all API endpoints](https://github.com/zulip/zulip/issues/732)
* [Build automated tests for the client API bindings](https://github.com/zulip/zulip/issues/713)
* [Add Python static type-checking to Zulip using mypy](https://github.com/zulip/zulip/issues/733)
* [Improve the runtime of Zulip's backend test suite](https://github.com/zulip/zulip/issues/441)
* [Use caching to make Travis CI runtimes faster](https://github.com/zulip/zulip/issues/712)
* [Add automated tests for the production upgrade process](https://github.com/zulip/zulip/issues/306)
* [Improve Travis CI "production" test suite to catch more regressions](https://github.com/zulip/zulip/issues/598)
## Development environment
* [Migrate from jslint to eslint](https://github.com/zulip/zulip/issues/535)
* [Figure out a nice upgrade process for Zulip Vagrant VMs](https://github.com/zulip/zulip/issues/264)
* [Overhaul new contributor documentation](https://github.com/zulip/zulip/issues/677)
* [Replace closure-compiler with a faster minifier toolchain](https://github.com/zulip/zulip/issues/693)
* [Add support for building frontend features in React](https://github.com/zulip/zulip/issues/694)
* [Use a javascript bundler like webpack](https://github.com/zulip/zulip/issues/695)
## Documentation
* [Significantly expand documentation of the Zulip API and integrating
with Zulip.](https://github.com/zulip/zulip/issues/672)
* [Expand library of documentation on Zulip's feature set. Currently
most documentation is for either developers or system administrators.](https://github.com/zulip/zulip/issues/675)
* [Expand developer documentation with more tutorials explaining how to do
various types of projects.](https://github.com/zulip/zulip/issues/676)
* [Overhaul new contributor documentation, especially on coding style,
to better highlight and teach the important pieces.](https://github.com/zulip/zulip/issues/677)
* [Update all screenshots to show the current Zulip UI](https://github.com/zulip/zulip/issues/599)
## Integrations
Integrations are essential to Zulip. While we currently have a
reasonably good framework for writing new webhook integrations for
getting notifications into Zulip, it'd be great to streamline that
process and make bots that receive messages just as easy to build.
* [Make it super easy to take screenshots for new webhook integrations](https://github.com/zulip/zulip/issues/658)
* [Add an outgoing webhook integration system](https://github.com/zulip/zulip/issues/735)
* [Build a framework to cut duplicated code in new webhook integrations](https://github.com/zulip/zulip/issues/660)
* [Make setting up a new integration a smooth flow](https://github.com/zulip/zulip/issues/692)
* [Optimize the integration writing documentation to make writing new
ones really easy.](https://github.com/zulip/zulip/issues/70)
## Android app
The Zulip Android app is ahead of the iOS app in terms of feature set,
so this section serves to document the goals for Zulip on mobile.
* [Support using a non-zulip.com server](https://github.com/zulip/zulip-android/issues/1)
* [Support Google authentication with a non-Zulip.com server](https://github.com/zulip/zulip-android/issues/49)
* [Add support for narrowing to @-mentions](https://github.com/zulip/zulip-android/issues/39)
* [Support having multiple Zulip realms open simultaneously](https://github.com/zulip/zulip-android/issues/47)
* [Build a slick development login page to simplify testing (similar to
the development homepage on web)](https://github.com/zulip/zulip-android/issues/48)
* [Improve the compose box to let you see what you're replying to](https://github.com/zulip/zulip-android/issues/8)
* [Make it easy to compose messages with mentions, emoji, etc.](https://github.com/zulip/zulip-android/issues/11)
* [Display unread counts and improve navigation](https://github.com/zulip/zulip-android/issues/57)
* [Hide messages sent to muted topics](https://github.com/zulip/zulip-android/issues/9)
* [Fill out documentation to make it easy to get started](https://github.com/zulip/zulip-android/issues/58)
## iOS app
Most of the projects listed under Android apply here as well, but it's
worth highlighting some areas where iOS is substantially behind
Android. The top priority here is recruiting a lead developer for the
iOS app. Once we have that resolved, we'll expand our ambitions for
the app with more specific improvements.
* [iOS app needs maintainer](https://github.com/zulip/zulip-ios/issues/12)
* [APNS notifications are broken](https://github.com/zulip/zulip/issues/538)
## Desktop apps
The top goal for the desktop apps is to rebuild it in modern toolchain
(probably Electron) so that it's easy for a wide range of developers
to contribute to the apps.
* Migrate platform from QT/webkit to Electron
* Desktop app doesn't recover well from entering the wrong Zulip server
* Support having multiple Zulip realms open simultaneously
* Build an efficient process for testing and releasing new versions of
the desktop apps
## Community
These don't get GitHub issues since they're not technical projects,
but they are important goals for the project.
* Setup a Zulip server for the Zulip development community
* Expand the number of core developers able to do code reviews
* Expand the number of contributors regularly adding features to Zulip
* Have a successful summer with Zulip's 3 GSOC students

23
docs/schema-migrations.md Normal file
View File

@@ -0,0 +1,23 @@
# Schema Migrations
Zulip uses the [standard Django system for doing schema
migrations](https://docs.djangoproject.com/en/1.8/topics/migrations/).
There is some example usage in the Zulip new feature tutorial on
readthedocs.
This page documents some important issues related to writing schema
migrations.
* **Large tables**: For large tables like Message and UserMessage, you
want to take precautions when adding columns to the table,
performing data backfills, or building indexes. We have a
`zerver/lib/migrate.py` library to help with adding columns and
backfilling data. For building indexes on these tables, we should do
this using SQL with postgres's CONCURRENTLY keyword.
* **Numbering conflicts across branches**: If you've done your schema
change in a branch, and meanwhile another schema change has taken
place, Django will now have two migrations with the same number. To
fix this, you can just rename the file, as long as no other
migrations depend on it (in which case you also need to update the
dependencies).

View File

@@ -15,7 +15,7 @@ Schema and initial data changes
-------------------------------
If you change the database schema or change the initial test data, you
have have to regenerate the pristine test database by running
have to regenerate the pristine test database by running
``tools/do-destroy-rebuild-test-database``.
Wiping the test databases
@@ -53,8 +53,8 @@ it. On Ubuntu:
Backend Django tests
--------------------
These live in ``zerver/tests.py`` and ``zerver/test_*.py``. Run them
with ``tools/test-backend``.
These live in ``zerver/tests/tests.py`` and
``zerver/tests/test_*.py``. Run them with ``tools/test-backend``.
Web frontend black-box casperjs tests
-------------------------------------
@@ -117,7 +117,7 @@ below:
collect a series of steps (each being a ``casper.then`` or
``casper.wait...`` call). Then, usually at the end of the test
file, you'll have a ``casper.run`` call which actually runs that
series of steps. This means that if you If you write code in your
series of steps. This means that if you write code in your
test file outside a ``casper.then`` or ``casper.wait...`` method, it
will actually run before all the Casper test steps that are declared
in the file, which can lead to confusing failures where the new code
@@ -321,3 +321,47 @@ Setting up the manual testing database
Will populate your local database with all the usual accounts plus some
test messages involving Shakespeare characters.
(This is run automatically as part of the development environment setup
process.)
Javascript manual testing
-------------------------
`debug.js` has some tools for profiling Javascript code, including:
- `print_elapsed_time`: Wrap a function with it to print the time that
function takes to the javascript console.
- `IterationProfiler`: Profile part of looping constructs (like a for
loop or $.each). You mark sections of the iteration body and the
IterationProfiler will sum the costs of those sections over all
iterations.
Chrome has a very good debugger and inspector in its developer tools.
Firebug for Firefox is also pretty good. They both have profilers, but
Chrome's is a sampling profiler while Firebug's is an instrumenting
profiler. Using them both can be helpful because they provide
different information.
Python 3 Compatibility
======================
Zulip is working on supporting Python 3, and all new code in Zulip
should be Python 2+3 compatible. We have converted most of the
codebase to be compatible with Python 3 using a suite of 2to3
conversion tools and some manual work. In order to avoid regressions
in that compatibility as we continue to develop new features in zulip,
we have a special tool, `tools/check-py3`, which checks all code for
Python 3 syntactic compatibility by running a subset of the automated
migration tools and checking if they trigger any changes.
`tools/check-py3` is run automatically in Zulip's Travis CI tests to
avoid any regressions, but is not included in `test-all` since it is
quite slow.
To run `tooks/check-py3`, you need to install the `modernize` and
`future` python packages (which are in the development environment's
`requirements.txt` file).
To run `check-py3` on just the python files in a particular directory,
you can change the current working directory (e.g. `cd zerver/`) and
run `check-py3` from there.

19
docs/translating.md Normal file
View File

@@ -0,0 +1,19 @@
# Translating Zulip
Zulip has full support for unicode, so you can already use your
preferred language everywhere in Zulip.
To make Zulip even better for users around the world, the Zulip UI is
being translated into a number of major languages, including Spanish,
German, French, Chinese, Russian, and Japanese, with varying levels of
progress. If you speak a language other than English, your help with
translating Zulip would be greatly appreciated!
If you're interested in contributing translations to Zulip, join the
[Zulip project on Transifex](https://www.transifex.com/zulip/zulip/)
and ask to join any languages you'd like to contribute to (or add new
ones). Transifex's notification system sometimes fails to notify the
maintainers when you ask to join a project, so please send a quick
email to zulip-core@googlegroups.com when you request to join the
project or add a language so that we can be sure to accept your
request to contribute.

View File

@@ -129,11 +129,11 @@ exports.then_send_message = function (type, params) {
else {
casper.test.assertTrue(false, "send_message got valid message type");
}
casper.fill('form[action^="/json/send_message"]', params);
casper.fill('form[action^="/json/messages"]', params);
casper.click('#compose-send-button');
});
casper.waitFor(function emptyComposeBox() {
return casper.getFormValues('form[action^="/json/send_message"]').content === '';
return casper.getFormValues('form[action^="/json/messages"]').content === '';
}, function () {
last_send_or_update = timestamp();
});

View File

@@ -20,7 +20,7 @@ common.then_send_many([
{ stream: 'Verona', subject: 'other subject',
content: 'test message C' },
{ stream: 'Venice', subject: 'frontend test',
{ stream: 'Denmark', subject: 'frontend test',
content: 'other message' },
{ recipient: 'cordelia@zulip.com, hamlet@zulip.com',
@@ -83,7 +83,7 @@ function expect_stream_subject() {
function expect_subject() {
common.expected_messages('zfilt', [
'Verona > frontend test',
'Venice > frontend test',
'Denmark > frontend test',
'Verona > frontend test'
], [
'<p>test message A</p>',
@@ -125,12 +125,20 @@ function expect_all_pm() {
]);
}
function check_narrow_title(title) {
return function () {
// need to get title tag from HTML
// test if it's equal to some string passed in to function
casper.test.assertSelectorHasText('title', title, 'Got expected narrow title');
};
}
function un_narrow() {
casper.then(common.un_narrow);
casper.then(expect_home);
casper.then(check_narrow_title('home - Zulip Dev - Zulip'));
}
// Narrow by clicking links.
common.wait_for_receive(function () {
@@ -141,6 +149,7 @@ common.wait_for_receive(function () {
casper.waitUntilVisible('#zfilt', function () {
expect_stream();
});
casper.then(check_narrow_title('Verona - Zulip Dev - Zulip'));
un_narrow();
casper.waitUntilVisible('#zhome', function () {
@@ -148,6 +157,7 @@ casper.waitUntilVisible('#zhome', function () {
casper.test.info('Narrowing by clicking subject');
casper.click('*[title="Narrow to stream \\\"Verona\\\", topic \\\"frontend test\\\""]');
});
casper.then(check_narrow_title('frontend test - Zulip Dev - Zulip'));
casper.waitUntilVisible('#zfilt', function () {
expect_stream_subject();
@@ -163,6 +173,7 @@ casper.waitUntilVisible('#zhome', function () {
casper.click('*[title="Narrow to your private messages with Cordelia Lear, King Hamlet"]');
});
casper.then(check_narrow_title('private - Zulip Dev - Zulip'));
casper.waitUntilVisible('#zfilt', function () {
expect_huddle();
@@ -205,32 +216,39 @@ function do_search(str, item) {
});
}
function search_and_check(str, item, check) {
function search_and_check(str, item, check, narrow_title) {
do_search(str, item);
casper.then(check);
casper.then(check_narrow_title(narrow_title));
un_narrow();
}
casper.waitUntilVisible('#zhome', expect_home);
// Test stream / recipient autocomplete in the search bar
search_and_check('Verona', 'Narrow to stream', expect_stream);
search_and_check('Cordelia', 'Narrow to private', expect_1on1);
search_and_check('Verona', 'Narrow to stream', expect_stream,
'Verona - Zulip Dev - Zulip');
search_and_check('Cordelia', 'Narrow to private', expect_1on1,
'private - Zulip Dev - Zulip');
// Test operators
search_and_check('stream:verona', 'Narrow', expect_stream);
search_and_check('stream:verona subject:frontend+test', 'Narrow', expect_stream_subject);
search_and_check('subject:frontend+test', 'Narrow', expect_subject);
search_and_check('stream:Verona', 'Narrow', expect_stream,
'Verona - Zulip Dev - Zulip');
search_and_check('stream:Verona subject:frontend+test', 'Narrow', expect_stream_subject,
'frontend test - Zulip Dev - Zulip');
search_and_check('subject:frontend+test', 'Narrow', expect_subject,
'home - Zulip Dev - Zulip');
// Narrow by clicking the left sidebar.
casper.then(function () {
casper.test.info('Narrowing with left sidebar');
});
casper.thenClick('#stream_filters [data-name="Verona"] a', expect_stream);
casper.then(check_narrow_title('Verona - Zulip Dev - Zulip'));
casper.thenClick('#global_filters [data-name="home"] a', expect_home);
casper.then(check_narrow_title('home - Zulip Dev - Zulip'));
casper.thenClick('#global_filters [data-name="private"] a', expect_all_pm);
casper.then(check_narrow_title('private - Zulip Dev - Zulip'));
un_narrow();

View File

@@ -68,6 +68,29 @@ casper.waitForSelector('.user_row[id="user_new-user-bot@zulip.com"]:not(.deactiv
casper.test.assertSelectorHasText('.user_row[id="user_new-user-bot@zulip.com"]', 'Deactivate');
});
// Test custom realm emoji
casper.waitForSelector('.admin-emoji-form', function () {
casper.fill('form.admin-emoji-form', {
'name': 'MouseFace',
'url': 'http://localhost:9991/static/images/integrations/logos/jenkins.png'
});
casper.click('form.admin-emoji-form input.btn');
});
casper.waitUntilVisible('div#admin-emoji-status', function () {
casper.test.assertSelectorHasText('div#admin-emoji-status', 'Custom emoji added!');
});
casper.waitForSelector('.emoji_row', function () {
casper.test.assertSelectorHasText('.emoji_row .emoji_name', 'MouseFace');
casper.test.assertExists('.emoji_row img[src="http://localhost:9991/static/images/integrations/logos/jenkins.png"]');
casper.click('.emoji_row button.delete');
});
casper.waitWhileSelector('.emoji_row', function () {
casper.test.assertDoesntExist('.emoji_row');
});
// TODO: Test stream deletion
common.then_log_out();

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env python2.7
from __future__ import print_function
import json
import os
import subprocess

View File

@@ -3,7 +3,8 @@ var path = require('path');
var fs = require('fs');
set_global('page_params', {realm_emoji: {
burrito: 'static/third/gemoji/images/emoji/burrito.png'
burrito: {display_url: 'static/third/gemoji/images/emoji/burrito.png',
source_url: 'static/third/gemoji/images/emoji/burrito.png'}
}});
add_dependencies({

View File

@@ -339,7 +339,7 @@ function get_predicate(operators) {
assert_same_operators(result, operators);
}
string ='stream:Foo topic:Bar yo';
string = 'stream:Foo topic:Bar yo';
operators = [
{operator: 'stream', operand: 'Foo'},
{operator: 'topic', operand: 'Bar'},

View File

@@ -18,8 +18,9 @@ set_global('$', function () {
});
set_global('feature_flags', {});
set_global('Filter', function () {});
var MessageList = require('js/message_list');
var MessageList = require('js/message_list').MessageList;
(function test_basics() {
var table;
@@ -60,7 +61,7 @@ var MessageList = require('js/message_list');
assert.equal(list.closest_id(60), 60);
assert.equal(list.closest_id(61), 60);
assert.deepEqual(list.all(), messages);
assert.deepEqual(list.all_messages(), messages);
global.$.Event = function (ev) {
assert.equal(ev, 'message_selected.zulip');
@@ -95,13 +96,13 @@ var MessageList = require('js/message_list');
list.view.clear_table = function () {};
list.remove_and_rerender([{id: 60}]);
var removed = list.all().filter(function (msg) {
var removed = list.all_messages().filter(function (msg) {
return msg.id !== 60;
});
assert.deepEqual(list.all(), removed);
assert.deepEqual(list.all_messages(), removed);
list.clear();
assert.deepEqual(list.all(), []);
assert.deepEqual(list.all_messages(), []);
}());
@@ -165,4 +166,4 @@ var MessageList = require('js/message_list');
assert.equal(list.closest_id(51), 50.02);
assert.equal(list.closest_id(59), 60);
assert.equal(list.closest_id(50.01), 50.01);
}());
}());

View File

@@ -1,6 +1,9 @@
set_global('page_params', {
domain: 'zulip.com'
});
add_dependencies({
unread: 'js/unread.js'
});
var muting = require('js/muting.js');

View File

@@ -0,0 +1,121 @@
set_global('$', function () {});
set_global('document', {
hasFocus: function () {
return true;
}
});
set_global('feature_flags', {});
set_global('page_params', {
people_list: []
});
add_dependencies({
Handlebars: 'handlebars',
templates: 'js/templates',
util: 'js/util.js',
compose_fade: 'js/compose_fade.js',
people: 'js/people.js',
unread: 'js/unread.js',
activity: 'js/activity.js'
});
var compose_fade = require('js/compose_fade.js');
compose_fade.update_faded_users = function () {
return;
};
global.$ = require('jQuery');
$.fn.expectOne = function () {
assert(this.length === 1);
return this;
};
global.use_template('user_presence_row');
global.use_template('user_presence_rows');
var people = require("js/people.js");
var activity = require('js/activity.js');
activity.presence_info = {
'alice@zulip.com': {status: activity.IDLE},
'fred@zulip.com': {status: activity.ACTIVE},
'jill@zulip.com': {status: activity.ACTIVE},
'mark@zulip.com': {status: activity.IDLE},
'norbert@zulip.com': {status: activity.ACTIVE}
};
(function test_presence_list_full_update() {
var users = activity.update_users();
assert.deepEqual(users, [
{ name: 'Fred Flintstone',
email: 'fred@zulip.com',
num_unread: 0,
type: 'active',
type_desc: 'is active',
mobile: undefined },
{ name: 'Jill Hill',
email: 'jill@zulip.com',
num_unread: 0,
type: 'active',
type_desc: 'is active',
mobile: undefined },
{ name: 'Norbert Oswald',
email: 'norbert@zulip.com',
num_unread: 0,
type: 'active',
type_desc: 'is active',
mobile: undefined },
{ name: 'Alice Smith',
email: 'alice@zulip.com',
num_unread: 0,
type: 'idle',
type_desc: 'is not active',
mobile: undefined },
{ name: 'Marky Mark',
email: 'mark@zulip.com',
num_unread: 0,
type: 'idle',
type_desc: 'is not active',
mobile: undefined }
]);
}());
(function test_presence_list_partial_update() {
var users = {
'alice@zulip.com': {status: 'active'}
};
activity.presence_info['alice@zulip.com'] = users['alice@zulip.com'];
users = activity.update_users(users);
assert.deepEqual(users, [
{ name: 'Alice Smith',
email: 'alice@zulip.com',
num_unread: 0,
type: 'active',
type_desc: 'is active',
mobile: undefined }
]);
// Test if user index in presence_info is the expected one
var all_users = activity._filter_and_sort(activity.presence_info);
assert.equal(all_users.indexOf('alice@zulip.com'), 0);
// Test another user
users = {
'mark@zulip.com': {status: 'active'}
};
activity.presence_info['mark@zulip.com'] = users['mark@zulip.com'];
users = activity.update_users(users);
assert.deepEqual(users, [
{ name: 'Marky Mark',
email: 'mark@zulip.com',
num_unread: 0,
type: 'active',
type_desc: 'is active',
mobile: undefined }
]);
all_users = activity._filter_and_sort(activity.presence_info);
assert.equal(all_users.indexOf('mark@zulip.com'), 3);
}());

View File

@@ -29,7 +29,9 @@ set_global('home_msg_list', {
selected_id: function () {return 1;}
});
set_global('page_params', {test_suite: false});
set_global('reload', {
is_in_progress: function () {return false;}
});
var server_events = require('js/server_events.js');

View File

@@ -13,6 +13,9 @@ add_dependencies({
set_global('recent_subjects', new global.Dict());
set_global('unread', {});
set_global('message_store', {
recent_private_messages: new global.Array()
});
var stream_list = require('js/stream_list.js');
@@ -23,6 +26,7 @@ $.fn.expectOne = function () {
};
global.use_template('sidebar_subject_list');
global.use_template('sidebar_private_message_list');
global.use_template('stream_sidebar_row');
global.use_template('stream_privacy');
@@ -46,6 +50,29 @@ global.use_template('stream_privacy');
assert.equal(topic, 'coding');
}());
(function test_build_private_messages_list() {
var reply_tos = "alice@zulip.com,bob@zulip.com";
var active_conversation = "Alice, Bob";
var max_conversations = 5;
var conversations = {reply_to: reply_tos,
display_reply_to: active_conversation,
timestamp: 0 };
global.message_store.recent_private_messages.push(conversations);
global.unread.num_unread_for_person = function () {
return 1;
};
var convos_html = stream_list._build_private_messages_list(active_conversation, max_conversations);
global.write_test_output("test_build_private_messages_list", convos_html);
var conversation = $(convos_html).find('a').text().trim();
assert.equal(conversation, active_conversation);
}());
(function test_add_stream_to_sidebar() {
// Make a couple calls to add_stream_to_sidebar() and make sure they
// generate the right markup as well as play nice with get_stream_li().

View File

@@ -630,6 +630,7 @@ function render(template_name, args) {
}());
(function user_presence_rows() {
global.use_template('user_presence_row'); // partial
var args = {
users: [
{
@@ -727,6 +728,30 @@ function render(template_name, args) {
}());
(function admin_emoji_list() {
global.use_template('admin_emoji_list');
var args = {
emoji: {
"name": "MouseFace",
"display_url": "http://emojipedia-us.s3.amazonaws.com/cache/46/7f/467fe69069c408e07517621f263ea9b5.png",
"source_url": "http://emojipedia-us.s3.amazonaws.com/cache/46/7f/467fe69069c408e07517621f263ea9b5.png"
}
};
var html = '';
html += '<tbody id="admin_emoji_table">';
html += render('admin_emoji_list', args);
html += '</tbody>';
global.write_test_output('admin_emoji_list.handlebars', html);
var emoji_name = $(html).find('tr.emoji_row:first span.emoji_name');
var emoji_url = $(html).find('tr.emoji_row:first span.emoji_image img');
assert.equal(emoji_name.text(), 'MouseFace');
assert.equal(emoji_url.attr('src'), 'http://emojipedia-us.s3.amazonaws.com/cache/46/7f/467fe69069c408e07517621f263ea9b5.png');
}());
// By the end of this test, we should have compiled all our templates. Ideally,
// we will also have exercised them to some degree, but that's a little trickier
// to enforce.

View File

@@ -8,7 +8,8 @@
// dependencies (except _).
add_dependencies({
muting: 'js/muting.js'
muting: 'js/muting.js',
unread: 'js/unread.js'
});
var stream_data = require('js/stream_data.js');
@@ -45,7 +46,7 @@ var zero_counts = {
narrow.active = function () {
return true;
};
current_msg_list.all = function () {
current_msg_list.all_messages = function () {
return [];
};
@@ -57,7 +58,7 @@ var zero_counts = {
narrow.active = function () {
return false;
};
current_msg_list.all = function () {
current_msg_list.all_messages = function () {
return [];
};
@@ -362,7 +363,7 @@ var zero_counts = {
var message = {
id: 15
};
current_msg_list.all = function () {
current_msg_list.all_messages = function () {
return [message];
};

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env python2.7
from __future__ import print_function
import subprocess
import requests
import optparse
@@ -50,7 +51,7 @@ server = subprocess.Popen(('tools/run-dev.py', '--test'),
def assert_server_running():
# Get the exit code of the server, or None if it is still running.
if server.poll() is not None:
raise RuntimeError, 'Server died unexpectedly! Check frontend_tests/casper_tests/server.log'
raise RuntimeError('Server died unexpectedly! Check frontend_tests/casper_tests/server.log')
def server_is_up():
assert_server_running()
@@ -80,18 +81,18 @@ try:
cmd += ' '.join(test_files)
else:
cmd += 'frontend_tests/casper_tests'
print "Running %s" % (cmd,)
print("Running %s" % (cmd,))
ret = subprocess.call(cmd, shell=True)
finally:
assert_server_running()
server.terminate()
if ret != 0:
print >>sys.stderr, """
print("""
Oops, the frontend tests failed. Tips for debugging:
* Check the frontend test server logs at frontend_tests/casper_tests/server.log
* Check the screenshots of failed tests at /tmp/casper-failure*.png
* Try remote debugging the test web browser as described in docs/testing.rst
"""
""", file=sys.stderr)
sys.exit(ret)

View File

@@ -9,6 +9,7 @@ if __name__ == "__main__":
from django.core.management.base import CommandError
raise CommandError("manage.py should not be run as root.")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "zproject.settings")
os.environ.setdefault("PYTHONSTARTUP", os.path.join(os.path.dirname(__file__), "scripts/lib/pythonrc.py"))
from django.conf import settings

View File

@@ -7,6 +7,7 @@
"dependencies": {},
"devDependencies": {
"handlebars": "1.3.0",
"istanbul": "0.4.0",
"jQuery": "1.7.4",
"jsdom": "0.5.7",
"xmlhttprequest": "1.5.0",

View File

@@ -3,66 +3,96 @@ import os
import sys
import logging
import platform
import subprocess
try:
import sh
except ImportError:
import pbs as sh
os.environ["PYTHONUNBUFFERED"] = "y"
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from zulip_tools import run
SUPPORTED_PLATFORMS = {
"Ubuntu": [
"trusty",
"xenial",
],
}
APT_DEPENDENCIES = {
"trusty": [
"closure-compiler",
"libfreetype6-dev",
"libffi-dev",
"memcached",
"rabbitmq-server",
"libldap2-dev",
"redis-server",
"postgresql-server-dev-all",
"libmemcached-dev",
"postgresql-9.3",
"python-dev",
"hunspell-en-us",
"nodejs",
"nodejs-legacy",
"python-virtualenv",
"supervisor",
"git",
"npm",
"yui-compressor",
"puppet", # Used by lint-all
"gettext", # Used by makemessages i18n
]
}
VENV_PATH = "/srv/zulip-venv"
PY3_VENV_PATH = "/srv/zulip-py3-venv"
ZULIP_PATH = os.path.dirname(os.path.abspath(__file__))
VENV_PATH="/srv/zulip-venv"
ZULIP_PATH="/srv/zulip"
if not os.path.exists(os.path.join(os.path.dirname(__file__), ".git")):
print("Error: No Zulip git repository present at /srv/zulip!")
if not os.path.exists(os.path.join(ZULIP_PATH, ".git")):
print("Error: No Zulip git repository present!")
print("To setup the Zulip development environment, you should clone the code")
print("from GitHub, rather than using a Zulip production release tarball.")
sys.exit(1)
# TODO: Parse arguments properly
if "--travis" in sys.argv:
ZULIP_PATH="."
if platform.architecture()[0] == '64bit':
arch = 'amd64'
elif platform.architecture()[0] == '32bit':
arch = "i386"
else:
logging.critical("Only x86 is supported; ping zulip-devel@googlegroups.com if you want another architecture.")
sys.exit(1)
# Ideally we wouldn't need to install a dependency here, before we
# know the codename.
subprocess.check_call(["sudo", "apt-get", "install", "-y", "lsb-release"])
vendor = subprocess.check_output(["lsb_release", "-is"]).strip()
codename = subprocess.check_output(["lsb_release", "-cs"]).strip()
if not (vendor in SUPPORTED_PLATFORMS and codename in SUPPORTED_PLATFORMS[vendor]):
logging.critical("Unsupported platform: {} {}".format(vendor, codename))
sys.exit(1)
POSTGRES_VERSION_MAP = {
"trusty": "9.3",
"xenial": "9.5",
}
POSTGRES_VERSION = POSTGRES_VERSION_MAP[codename]
UBUNTU_COMMON_APT_DEPENDENCIES = [
"closure-compiler",
"libfreetype6-dev",
"libffi-dev",
"memcached",
"rabbitmq-server",
"libldap2-dev",
"redis-server",
"postgresql-server-dev-all",
"libmemcached-dev",
"python-dev",
"hunspell-en-us",
"nodejs",
"nodejs-legacy",
"python-virtualenv",
"supervisor",
"git",
"npm",
"yui-compressor",
"wget",
"ca-certificates", # Explicit dependency in case e.g. wget is already installed
"puppet", # Used by lint-all
"gettext", # Used by makemessages i18n
"curl", # Used for fetching PhantomJS as wget occasionally fails on redirects
"netcat", # Used for flushing memcached
]
APT_DEPENDENCIES = {
"trusty": UBUNTU_COMMON_APT_DEPENDENCIES + [
"postgresql-9.3",
],
"xenial": UBUNTU_COMMON_APT_DEPENDENCIES + [
"postgresql-9.5",
],
}
# tsearch-extras is an extension to postgres's built-in full-text search.
# TODO: use a real APT repository
TSEARCH_URL_BASE = "https://dl.dropboxusercontent.com/u/283158365/zuliposs/"
TSEARCH_PACKAGE_NAME = {
"trusty": "postgresql-9.3-tsearch-extras"
}
TSEARCH_VERSION = "0.1.2"
# TODO: this path is platform-specific!
TSEARCH_STOPWORDS_PATH = "/usr/share/postgresql/9.3/tsearch_data/"
TSEARCH_URL_PATTERN = "https://github.com/zulip/zulip-dist-tsearch-extras/raw/master/{}_{}_{}.deb?raw=1"
TSEARCH_PACKAGE_NAME = "postgresql-%s-tsearch-extras" % (POSTGRES_VERSION,)
TSEARCH_VERSION = "0.1.3"
TSEARCH_URL = TSEARCH_URL_PATTERN.format(TSEARCH_PACKAGE_NAME, TSEARCH_VERSION, arch)
TSEARCH_STOPWORDS_PATH = "/usr/share/postgresql/%s/tsearch_data/" % (POSTGRES_VERSION,)
REPO_STOPWORDS_PATH = os.path.join(
ZULIP_PATH,
"puppet",
@@ -74,67 +104,24 @@ REPO_STOPWORDS_PATH = os.path.join(
LOUD = dict(_out=sys.stdout, _err=sys.stderr)
def main():
log = logging.getLogger("zulip-provisioner")
# TODO: support other architectures
if platform.architecture()[0] == '64bit':
arch = 'amd64'
else:
log.critical("Only amd64 is supported.")
run(["sudo", "apt-get", "update"])
run(["sudo", "apt-get", "-y", "install"] + APT_DEPENDENCIES[codename])
vendor, version, codename = platform.dist()
temp_deb_path = subprocess.check_output(["mktemp", "package_XXXXXX.deb", "--tmpdir"])
run(["wget", "-O", temp_deb_path, TSEARCH_URL])
run(["sudo", "dpkg", "--install", temp_deb_path])
if not (vendor in SUPPORTED_PLATFORMS and codename in SUPPORTED_PLATFORMS[vendor]):
log.critical("Unsupported platform: {} {}".format(vendor, codename))
run(["sudo", "rm", "-rf", VENV_PATH])
run(["sudo", "mkdir", "-p", VENV_PATH])
run(["sudo", "chown", "{}:{}".format(os.getuid(), os.getgid()), VENV_PATH])
with sh.sudo:
sh.apt_get.update(**LOUD)
sh.apt_get.install(*APT_DEPENDENCIES["trusty"], assume_yes=True, **LOUD)
temp_deb_path = sh.mktemp("package_XXXXXX.deb", tmpdir=True)
sh.wget(
"{}/{}_{}_{}.deb".format(
TSEARCH_URL_BASE,
TSEARCH_PACKAGE_NAME["trusty"],
TSEARCH_VERSION,
arch,
),
output_document=temp_deb_path,
**LOUD
)
with sh.sudo:
sh.dpkg("--install", temp_deb_path, **LOUD)
with sh.sudo:
PHANTOMJS_PATH = "/srv/phantomjs"
PHANTOMJS_TARBALL = os.path.join(PHANTOMJS_PATH, "phantomjs-1.9.8-linux-x86_64.tar.bz2")
sh.mkdir("-p", PHANTOMJS_PATH, **LOUD)
sh.wget("https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-1.9.8-linux-x86_64.tar.bz2",
output_document=PHANTOMJS_TARBALL, **LOUD)
sh.tar("xj", directory=PHANTOMJS_PATH, file=PHANTOMJS_TARBALL, **LOUD)
sh.ln("-sf", os.path.join(PHANTOMJS_PATH, "phantomjs-1.9.8-linux-x86_64", "bin", "phantomjs"),
"/usr/local/bin/phantomjs", **LOUD)
with sh.sudo:
sh.rm("-rf", VENV_PATH, **LOUD)
sh.mkdir("-p", VENV_PATH, **LOUD)
sh.chown("{}:{}".format(os.getuid(), os.getgid()), VENV_PATH, **LOUD)
sh.virtualenv(VENV_PATH, **LOUD)
# Add the ./tools and ./scripts/setup directories inside the repository root to
# the system path; we'll reference them later.
orig_path = os.environ["PATH"]
os.environ["PATH"] = os.pathsep.join((
os.path.join(ZULIP_PATH, "tools"),
os.path.join(ZULIP_PATH, "scripts", "setup"),
orig_path
))
run(["sudo", "rm", "-rf", PY3_VENV_PATH])
run(["sudo", "mkdir", "-p", PY3_VENV_PATH])
run(["sudo", "chown", "{}:{}".format(os.getuid(), os.getgid()), PY3_VENV_PATH])
run(["virtualenv", VENV_PATH])
run(["virtualenv", "-p", "python3", PY3_VENV_PATH])
# Put Python virtualenv activation in our .bash_profile.
with open(os.path.expanduser('~/.bash_profile'), 'w+') as bash_profile:
@@ -143,32 +130,54 @@ def main():
"source %s\n" % (os.path.join(VENV_PATH, "bin", "activate"),),
])
# Switch current Python context to the virtualenv.
# Switch current Python context to the python3 virtualenv
activate_this = os.path.join(PY3_VENV_PATH, "bin", "activate_this.py")
execfile(activate_this, dict(__file__=activate_this))
run(["pip", "install", "--upgrade", "pip"])
# install requirement
run(["pip", "install", "--no-deps", "--requirement",
os.path.join(ZULIP_PATH, "tools", "py3_test_reqs.txt")])
# Switch current Python context to the python2 virtualenv.
activate_this = os.path.join(VENV_PATH, "bin", "activate_this.py")
execfile(activate_this, dict(__file__=activate_this))
sh.pip.install(requirement=os.path.join(ZULIP_PATH, "requirements.txt"), **LOUD)
run(["pip", "install", "--upgrade", "pip"])
run(["pip", "install", "--no-deps", "--requirement",
os.path.join(ZULIP_PATH, "requirements.txt")])
with sh.sudo:
sh.cp(REPO_STOPWORDS_PATH, TSEARCH_STOPWORDS_PATH, **LOUD)
run(["sudo", "cp", REPO_STOPWORDS_PATH, TSEARCH_STOPWORDS_PATH])
# npm install and management commands expect to be run from the root of the project.
# npm install and management commands expect to be run from the root of the
# project.
os.chdir(ZULIP_PATH)
sh.npm.install(**LOUD)
os.system("tools/download-zxcvbn")
os.system("tools/emoji_dump/build_emoji")
os.system("generate_secrets.py -d")
run(["tools/install-phantomjs"])
run(["tools/download-zxcvbn"])
run(["tools/emoji_dump/build_emoji"])
run(["scripts/setup/generate_secrets.py", "-d"])
if "--travis" in sys.argv:
os.system("sudo service rabbitmq-server restart")
os.system("sudo service redis-server restart")
os.system("sudo service memcached restart")
sh.configure_rabbitmq(**LOUD)
sh.postgres_init_dev_db(**LOUD)
sh.do_destroy_rebuild_database(**LOUD)
sh.postgres_init_test_db(**LOUD)
sh.do_destroy_rebuild_test_database(**LOUD)
run(["sudo", "service", "rabbitmq-server", "restart"])
run(["sudo", "service", "redis-server", "restart"])
run(["sudo", "service", "memcached", "restart"])
elif "--docker" in sys.argv:
run(["sudo", "service", "rabbitmq-server", "restart"])
run(["sudo", "pg_dropcluster", "--stop", POSTGRES_VERSION, "main"])
run(["sudo", "pg_createcluster", "-e", "utf8", "--start", POSTGRES_VERSION, "main"])
run(["sudo", "service", "redis-server", "restart"])
run(["sudo", "service", "memcached", "restart"])
run(["scripts/setup/configure-rabbitmq"])
run(["tools/postgres-init-dev-db"])
run(["tools/do-destroy-rebuild-database"])
run(["tools/postgres-init-test-db"])
run(["tools/do-destroy-rebuild-test-database"])
# Install the latest npm.
run(["sudo", "npm", "install", "-g", "npm"])
# Run npm install last because it can be flaky, and that way one
# only needs to rerun `npm install` to fix the installation.
run(["npm", "install"])
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@@ -84,4 +84,4 @@
"source": "https://github.com/puppetlabs/puppetlabs-apt",
"project_page": "https://github.com/puppetlabs/puppetlabs-apt",
"license": "Apache License 2.0"
}
}

View File

@@ -267,4 +267,4 @@
],
"author": "puppetlabs",
"name": "puppetlabs-stdlib"
}
}

View File

@@ -3,6 +3,7 @@
"""
Nagios plugin to check that none of our queue workers have reported errors.
"""
from __future__ import print_function
import sys
sys.path.append('/home/zulip/deployments/current')

View File

@@ -8,6 +8,7 @@ which is generated by bots/check-rabbitmq-consumers.
It is run by cron and can be found at bots/rabbitmq-numconsumers-crontab
"""
from __future__ import print_function
import sys
@@ -15,12 +16,12 @@ sys.path.append('/home/zulip/deployments/current')
from bots.cron_file_helper import nagios_from_file
if len(sys.argv) < 2:
print "Please pass the name of the consumer file to check"
print("Please pass the name of the consumer file to check")
exit(1)
RESULTS_FILE = "/var/lib/nagios_state/check-rabbitmq-consumers-%s" % (sys.argv[1])
ret, result = nagios_from_file(RESULTS_FILE)
print result
print(result)
exit(ret)

View File

@@ -9,6 +9,7 @@ which is generated by bots/check-rabbitmq-queue.
It is run by cron and can be found at bots/rabbitmq-queuesize-crontab
"""
from __future__ import print_function
import sys
@@ -18,5 +19,5 @@ from bots.cron_file_helper import nagios_from_file
RESULTS_FILE = "/var/lib/nagios_state/check-rabbitmq-results"
ret, result = nagios_from_file(RESULTS_FILE)
print result
print(result)
exit(ret)

View File

@@ -8,6 +8,8 @@ It supports both munin and nagios outputs
It must be run on a machine that is using the live database for the
Django ORM.
"""
from __future__ import print_function
from __future__ import division
import datetime
import sys
@@ -16,6 +18,8 @@ import random
import traceback
import os
import django
def total_seconds(timedelta):
return (timedelta.microseconds + (timedelta.seconds + timedelta.days * 24 * 3600) * 10**6) / 10.**6
@@ -40,23 +44,22 @@ parser.add_option('--munin',
(options, args) = parser.parse_args()
if not options.nagios and not options.munin:
print 'No output options specified! Please provide --munin or --nagios'
print('No output options specified! Please provide --munin or --nagios')
sys.exit(0)
if len(args) > 2:
print usage
print(usage)
sys.exit(0)
if options.munin:
if len(args) and args[0] == 'config':
print \
"""graph_title Send-Receive times
print("""graph_title Send-Receive times
graph_info The number of seconds it takes to send and receive a message from the server
graph_args -u 5 -l 0
graph_vlabel RTT (seconds)
sendreceive.label Send-receive round trip time
sendreceive.warning 3
sendreceive.critical 5"""
sendreceive.critical 5""")
sys.exit(0)
sys.path.append('/home/zulip/deployments/current/api')
@@ -64,6 +67,9 @@ import zulip
sys.path.append('/home/zulip/deployments/current')
os.environ['DJANGO_SETTINGS_MODULE'] = "zproject.settings"
django.setup()
from zerver.models import get_user_profile_by_email
from django.conf import settings
@@ -76,9 +82,9 @@ states = {
def report(state, time, msg=None):
if msg:
print "%s: %s" % (state, msg)
print("%s: %s" % (state, msg))
else:
print "%s: send time was %s" % (state, time)
print("%s: send time was %s" % (state, time))
exit(states[state])
def send_zulip(sender, message):
@@ -143,7 +149,7 @@ while msg_to_send not in msg_content:
msg_content = [m['content'] for m in messages]
print zulip_recipient.deregister(queue_id)
print(zulip_recipient.deregister(queue_id))
if options.nagios:
if time_diff.seconds > 3:
@@ -152,6 +158,6 @@ if options.nagios:
report('CRITICAL', time_diff)
if options.munin:
print "sendreceive.value %s" % total_seconds(time_diff)
print("sendreceive.value %s" % total_seconds(time_diff))
elif options.nagios:
report('OK', time_diff)

View File

@@ -1,7 +1,7 @@
#!/bin/bash
# Checks for any Zulip queue workers that are leaking memory and thus have a high vsize
datafile=$(mktemp)
ps -o vsize,size,pid,user,command --sort -vsize "$(pgrep -f '^python /home/zulip/deployments/current/manage.py process_queue')" > "$datafile"
ps -o vsize,size,pid,user,command --sort -vsize $(pgrep -f '^python.* /home/zulip/deployments/current/manage.py process_queue') > "$datafile"
cat "$datafile"
top_worker=$(cat "$datafile" | head -n2 | tail -n1)
top_worker_memory_usage=$(echo "$top_worker" | cut -f1 -d" ")

Some files were not shown because too many files have changed in this diff Show More