Compare commits

...

1050 Commits

Author SHA1 Message Date
Tim Abbott
4958828747 Add changelog for Zulip 1.3.13 release. 2016-06-21 16:59:48 -07:00
Tim Abbott
1d2d147a62 Update translations data from Transifex. 2016-06-21 16:45:55 -07:00
Tim Abbott
250781e843 Fix HTTP Basic Auth popups caused by auth failures.
If a user's session cookie expired, the next REST API request their
browser did would go into the json_unauthorized code path.  This
returned a response with a WWW-Authenticate tag for HTTP Basic Auth
(since that's what the REST API uses), even for /json requests which
should only be authenticated using session auth.

We fix this by explicitly passing the desired WWW-Authenticate state.

Fixes: #800.
2016-06-21 16:18:36 -07:00
Tim Abbott
45beac7d6c test_decorators: Add test for /json/fetch_api_key. 2016-06-21 16:14:22 -07:00
Tim Abbott
f39c9161fe Fix fetching user API keys via settings page when using LDAP backend.
Previously, json_fetch_api_key was hardcoding a check using Zulip's
built-in password functionality, rather than using authenticate().
2016-06-21 14:58:25 -07:00
Tim Abbott
1565ee8453 Fix using LDAP backend with Zulip mobile apps.
The recent changes to api_fetch_api_key to receive detailed data via
the "return_data" object did not properly update the LDAP backend to
accept that argument, causing mobile password authentication to not
work with the LDAP backend.
2016-06-21 14:57:54 -07:00
akashnimare
42ebf6acb7 Update Zulip /hello page to match zulip.org.
[with rebasing tweaks by tabbott].
2016-06-21 14:54:46 -07:00
Tomasz Kolek
7aa45ffa45 Increase send_message_backend coverage. 2016-06-21 14:33:21 -07:00
Vishnu Ks
055c7eed04 Tweak the CSS of skip-tutorial button. 2016-06-21 14:23:17 -07:00
Tim Abbott
50f723f50b Split test_narrow.py out of test_messages.py. 2016-06-21 12:25:08 -07:00
Tomasz Kolek
e3e03e2946 Add NarrowBuilder test cases.
Refactor existing add_term test cases.
2016-06-21 11:53:31 -07:00
Tomasz Kolek
f8c368c07f Add default-bot during populate_db. 2016-06-21 11:47:38 -07:00
Tomasz Kolek
9ae68ade8b Add is_webhook option to authentication decorats.
Modified:
authenticated_rest_api_view
authenticated_api_view and validate_api_key.
2016-06-21 11:47:38 -07:00
Tim Abbott
80d92c1651 Add comment documenting confusing list_to_streams code. 2016-06-21 11:45:35 -07:00
Eklavya Sharma
fc8d4f9ef5 Factor out venv-creating code from provision.py.
Move setup_virtualenv and do_setup_virtualenv from provision.py to
scripts/lib/setup_venv.py.
2016-06-21 11:25:41 -07:00
Alex Wilson
f68a392250 Run django.setup() after coverage.start() in tests to fix coverage report.
Fixes #1007.
2016-06-21 10:37:45 -07:00
Tim Abbott
2640cc44c7 Move coverage to dev requirements.txt. 2016-06-21 07:48:58 -07:00
Tim Abbott
5f958f46a5 Move pyflakes to dev requirements.txt. 2016-06-21 07:48:58 -07:00
Tim Abbott
1a876310bc Move ipython dependencies to their own requirements file. 2016-06-21 07:48:58 -07:00
Tim Abbott
09fcc079b4 Move moto dependencies to new requirements file. 2016-06-21 07:48:58 -07:00
Tim Abbott
fd1a134bef Move transifex dependencies to dev.txt. 2016-06-21 07:48:58 -07:00
Tim Abbott
b43bf10a93 Move recommonmark requirement to docs.txt. 2016-06-21 07:48:58 -07:00
Eklavya Sharma
be70907eaa Remove references to requirements.txt. 2016-06-21 07:48:24 -07:00
Eklavya Sharma
b5d08bb5de README.dev.md: Improve pip instructions.
Recommend using a virtualenv.
Add instruction to do `pip install --upgrade pip` before installing
python packages.
2016-06-21 07:35:26 -07:00
Umair Khan
4149b0bb56 Fix path to hash_req.py in provision.py.
Fixes #1081.
2016-06-21 07:01:45 -07:00
Akash Nimare
8939dc1cdd Fixed broken Docker linux installation link.
[This](https://docs.docker.com/engine/started/) link is broken. Updated to the latest.
2016-06-21 08:36:40 +05:30
krtkmj
9dadab6eac Replace placeholder variables x with more meaningful ones. 2016-06-20 19:36:07 -07:00
Tim Abbott
7370fd69d7 Move doc building dependencies to requirements/docs.txt. 2016-06-20 16:05:42 -07:00
medullaskyline
e2eb4e0b7e Annotate zerver/lib/email_mirror.py.
[With some fixes from @sharmaeklavya2].
2016-06-20 15:58:40 -07:00
Eklavya Sharma
a2668a2853 zerver/lib/notifications.py: Fix an annotation. 2016-06-21 02:20:36 +05:30
Umair Khan
c61a3dfbcc Ensure translations are loaded.
Since i18next loads translations asynchronously we need to make sure
that they are loaded before we call the JS code which depends on them.

Fixes #982
2016-06-20 11:31:28 -07:00
Umair Khan
d29ab6651b Mark strings translatable in the frontend. 2016-06-20 11:31:28 -07:00
Umair Khan
134b165b1a Fix frontend translation string bug.
The translation string which contain \n are not captured
within {{#tr}}{{/tr}} blocks. Fix is to escape the slash.
2016-06-20 11:31:28 -07:00
Eklavya Sharma
81ecfc5a43 Split requirements.txt into multiple files. 2016-06-20 11:09:20 -07:00
Eklavya Sharma
500cc32e64 provision.py: Use tools/hash_reqs.py.
Use tools/hash_reqs.py to generate hash of requirements.txt in
provision.py while setting up virtualenv.
2016-06-20 11:09:20 -07:00
Eklavya Sharma
8148cbe173 Add tools/hash_reqs.py.
tools/hash_reqs.py generates a hash of a requirements file.  It
does that by generating a sorted list of unique dependencies referred
to by that requirements file.  To do that, it also recurses into other
requirements files specified inside that requirements file.
2016-06-20 11:09:20 -07:00
Vishnu Ks
ff66ce780a Mention emails are printed in run-dev.py console.
Fixes: #1046
2016-06-20 11:07:20 -07:00
Tim Abbott
f7ce5fc179 generate_secrets: Silence mypy error with configparser.
See https://github.com/python/typeshed/issues/307.
2016-06-20 09:03:09 -07:00
Tim Abbott
eb71173be3 lint: Fix warnings under confirmation/. 2016-06-20 08:19:54 -07:00
Umair Khan
a9a6687b7d Make generate-secrets script use existing values.
Fixes #1035
2016-06-20 17:00:27 +05:00
Eklavya Sharma
aceee3da11 zerver/lib/rate_limiter.py: Annotate rate_limiter.rules.
After annotating rate_limiter.rules, mypy complained that rules does
not support cmp.  So use key to customize sort instead of cmp.
Python docs also recommend using key over cmp.
2016-06-18 16:41:41 -07:00
Eklavya Sharma
13f62da4ce zerver/lib/rate_limiter.py: Fix annotations. 2016-06-18 16:41:40 -07:00
Eklavya Sharma
6097f6eed5 zerver/lib/initial_password.py: Encode return value.
zerver.lib.initial_password.initial_password is supposed to return an
Optional[text_type], but it returns an Optional[binary_type] instead.
Encode the return value to make sure it returns an Optional[text_type].
2016-06-18 16:41:40 -07:00
Eklavya Sharma
018041625c zerver/lib/html_diff.py: Fix annotations. 2016-06-18 16:41:40 -07:00
Eklavya Sharma
68823767e2 zerver/lib/digest.py: Fix annotations. 2016-06-18 16:41:40 -07:00
Eklavya Sharma
64ccb390ff Annotate zerver/lib/create_user.py. 2016-06-18 16:41:40 -07:00
Tim Abbott
3899b4c913 Move py3k requirements to requirements/py3k.txt. 2016-06-18 16:41:40 -07:00
Tim Abbott
5c92639f81 Move mypy requirements to requirements/ directory. 2016-06-18 16:41:40 -07:00
Eklavya Sharma
666041e1b2 Remove wsgiref, argparse and South from requirements.txt.
wsgiref and argparse are part of the Python 2.7 standard library, and
South ceased being relevant in Django 1.6.
2016-06-18 15:50:06 -07:00
Tim Abbott
46c2416cc8 puppet: Add postgres version map for xenial.
This is an early step towards Ubuntu Xenial support in production.
2016-06-17 16:41:42 -07:00
Vishnu Ks
ad1c3894d9 Add interface for creating new realms.
This is controlled by settings.OPEN_REALM_CREATION; if that setting is
off, this feature doesn't do anything.
2016-06-17 16:15:28 -07:00
Vishnu Ks
8213ca135a Move default_stream list to settings. 2016-06-17 16:01:38 -07:00
Rishi Gupta
ce5e7d3ba6 Make minor edits to new settings documentation. 2016-06-17 12:15:15 -07:00
Tim Abbott
b5e92f541a Add new documentation on Zulip settings system.
Fixes: #962.
2016-06-17 12:15:15 -07:00
Tim Abbott
6d1d7471e6 Refactor out zproject/dev_settings.py. 2016-06-17 12:15:15 -07:00
Taranjeet
964ff027bd Dockerfile: Create symlink in home folder for project. 2016-06-17 11:48:59 -07:00
Taranjeet
3fae7a69b3 VagrantFile: Create symlink in home folder for project.
Fixes: #922.
2016-06-17 11:48:55 -07:00
Eklavya Sharma
14c4ff2d25 docs/code-style.md: Use ASCII spaces consistently.
At some places, '\xc2\xa0' was used instead of ' '.  This characters looks
identical, but can hamper searches and lint checks.
2016-06-17 11:33:41 -07:00
Tim Abbott
07abfc189b Update docs/README.md to reflect the current state of the world.
Fixes #1050.
2016-06-17 11:30:22 -07:00
kunall17
007eee6061 Add route to fetch emails for mobile passwordless login.
[Tweaked by tabbott to rename API to explicitly support not just
Android]
2016-06-17 11:03:19 -07:00
kunall17
7ea0eaed1c Add passwordless login for mobile app development.
[Tweaked by tabbott to rename API to explicitly support not just
Android].
2016-06-17 10:58:33 -07:00
Tim Abbott
d117ec8664 lint: Fix redundant stripping of strings. 2016-06-17 10:41:39 -07:00
Tim Abbott
267a71cf20 Run Python custom RE checks in parallel with non-python.
This saves about 1s (out of originally 3.3s) of time running all the
linters in my development environment.
2016-06-17 10:26:50 -07:00
Vishnu Ks
01c9bb2d5e Make name_changes_disabled() work with no argument. 2016-06-16 17:13:59 -07:00
Tim Abbott
e109b50152 Fix alignment of airbrake integration in urls.py. 2016-06-16 15:30:45 -07:00
Tomasz Kolek
76cbe89613 Add IFTTT integration. 2016-06-16 15:30:45 -07:00
Eklavya Sharma
598fb1ff28 zerver/lib/bugdown/fenced_code.py: Add BaseHandler.
Add a class 'BaseHandler' and make it a base class of OuterHandler,
QuoteHandler and CodeHandler.  This will help annotate some functions
and improve type checking.
2016-06-16 15:21:17 -07:00
Eklavya Sharma
142bcadb68 Annotate zerver/lib/bugdown/fenced_code.py. 2016-06-16 15:21:17 -07:00
Eklavya Sharma
9b72b7b37e Annotate zerver/lib/bugdown/codehilite.py.
Also change some string literals to unicode.
2016-06-16 15:21:17 -07:00
Eklavya Sharma
744007f33f zerver/lib/bugdown/__init__.py: Fix string types.
Change important string constants from str to text_type.
Replace str by text_type in annotations where relevant.
2016-06-16 15:21:17 -07:00
Eklavya Sharma
04f44b12ad zerver/lib/bugdown/testing_mocks.py: Fix types.
Also fix clashing annotations in zerver/lib/bugdown/__init__.py.
2016-06-16 15:21:17 -07:00
Eklavya Sharma
7db0765a18 zerver/lib/bugdown/fenced_code.py: Fix types.
Change some important string literals from str to unicode.
Annotate format_code and codehilite_conf in FencedBlockProcessor.
2016-06-16 15:21:17 -07:00
Eklavya Sharma
48c5b299b6 zerver/lib/bugdown/codehilite.py: Add type annotation.
Mypy incorrectly infers a dict's type as `Dict[str, List[object]]`.
Change that to `Dict[str, List[Any]]`.
2016-06-16 15:21:17 -07:00
Eklavya Sharma
a485d63975 zerver/lib/bugdown/__init__.py: Fix non-string annotations. 2016-06-16 15:21:17 -07:00
Tim Abbott
eafb91719c get_tweet_id: Fix return type to by None. 2016-06-16 15:21:01 -07:00
Tim Abbott
33df0b29d3 Annotate zerver/tests/test_hooks.py. 2016-06-16 14:07:34 -07:00
Tim Abbott
49ae0052b2 send_json_payload: payload arg can be text_type. 2016-06-16 14:07:34 -07:00
Tim Abbott
df9f89fe2c test_helpers: Change send_json_payload to use text_type. 2016-06-16 14:07:34 -07:00
Tim Abbott
b14eacd552 test_helpers: Change fixture_data to text_type. 2016-06-16 14:07:34 -07:00
Tim Abbott
5b05644c95 Run mypy on zerver/tests/test_hooks.py. 2016-06-16 14:07:34 -07:00
Tim Abbott
6723525fd3 Annotate zerver/tests/tests.py. 2016-06-16 14:07:34 -07:00
Tim Abbott
45883386ce AlertWordTests: Cleanup confusing message variable reuse. 2016-06-16 13:55:58 -07:00
John Hergenroeder
16a19226f6 Add linter check for redundant REQ whence argument. 2016-06-16 13:53:39 -07:00
Christie Koehler
a9b277368a requirements: Add dependencies required by Sphinx.
Since `pip install` is passed `--no-deps`, we need to explicitly list
it's dependencies in requirements.txt in order for docs generation to
work.
2016-06-16 09:39:47 -07:00
Taranjeet
219ecfff99 Readme.dev.md: Update broken link for docker architecture. 2016-06-16 09:02:02 -07:00
Tim Abbott
0071731915 test-run-dev: Display original error messages on failures.
This helped with debugging a failure recently.
2016-06-15 22:44:04 -07:00
Tim Abbott
b65c187398 Fix docker instructions using port 80 on localhost.
This was confusing, because we otherwise suggest consistently to users
that they should connect to their development environment on port
9991.

Fixes #1032.
2016-06-15 16:41:47 -07:00
Eklavya Sharma
98553e8caa zerver/lib/push_notifications.py: Fix strings.
Use appropriate string encode/decode operations and fix annotations.
2016-06-15 15:25:28 -07:00
Eklavya Sharma
78565a96c9 zerver/lib/notifications.py: Fix string annotations. 2016-06-15 15:25:28 -07:00
Tim Abbott
39a8c82957 travis: Remove verbose coverage report from end of build output. 2016-06-15 15:05:38 -07:00
Tim Abbott
10dd9addb7 Fix ChangeSettingsTest caching issue causing test failures. 2016-06-15 14:57:58 -07:00
Tim Abbott
06079042d4 ChangeSettingsTest: Move login to test helper method. 2016-06-15 14:28:24 -07:00
Tim Abbott
8f67b7e498 Simplify check_for_toggle_param. 2016-06-15 14:23:51 -07:00
Ashish Kumar
0a98d9edcf Add test_enter_sends_setting. 2016-06-15 14:23:51 -07:00
Ashish Kumar
8a55098ca7 Add test_time_setting. 2016-06-15 14:23:51 -07:00
Ashish Kumar
345df3538f Add test_toggling_left_side_userlist. 2016-06-15 14:23:51 -07:00
Ashish Kumar
24767302c4 Update test_ui_settings to use new abstraction. 2016-06-15 14:23:51 -07:00
Ashish Kumar
006e528e18 Update test_notify_settings to use new abstraction. 2016-06-15 14:23:51 -07:00
Ashish Kumar
5ae06e8c33 Added test helper function: check_for_toggle_param 2016-06-15 14:18:55 -07:00
Tim Abbott
ce938ca39c Annotate provision.py.
Annotations aren't particularly important here, but it will prevent
coveralls from complaining whenever someone patches provision.py.
2016-06-15 12:12:23 -07:00
Eklavya Sharma
553a9d0b75 tools/run-mypy: Exclude py files which have a pyi.
If a .py file has a corresponding .pyi stub file, exclude that
.py file from mypy.
2016-06-15 22:40:31 +05:30
Umair Khan
7d3dc5d0b3 Cache npm for Travis CI.
This should speed up all the major Zulip builds in Travis CI.

Fixes #712.
2016-06-15 09:32:50 -07:00
Tim Abbott
9e6b9dacf6 test-migrations: Fix missing exit status on error.
Previously this test was correctly detecting missing migrations, but
not causing the build to fail.
2016-06-15 09:26:04 -07:00
Tim Abbott
f88e5b7438 Add missing no-op migration for realm_emoji.
Because of how Django's migration system works, changing the error
message attached to a model field's validator results in an extra
migration.
2016-06-15 09:26:01 -07:00
Tim Abbott
7f06fec9d5 Fix spelling of _streams_deferred. 2016-06-14 16:36:54 -07:00
Tim Abbott
c7f0e66f7e Replace deprecated jQuery deferred.isResolved(). 2016-06-14 16:36:54 -07:00
Tim Abbott
c0e8f3f2bf [third] Upgrade underscore.js to version 1.8.3.
This seems to have no backwards-incompatible changes affecting us, and
likely brings a number of performance and compatibility improvements.
2016-06-14 16:36:54 -07:00
Tim Abbott
02e6d267f1 [third] Replace jquery-caret with newer implementation.
Apparently, there are like 5 independently developed jquery-caret
plugins, none of which are great.  The previous one we were using was
last modified in 2010.  This new one comes from
https://github.com/acdvorak/jquery.caret and at least doesn't use
deprecated jQuery syntax and has a repository on GitHub.

This plugin is way larger than it needs to be for what it does, but we
can deal with that later.
2016-06-14 16:36:51 -07:00
Umair Khan
a976ccefbf [third] Urlencode name of the uploaded file.
Update jquery-filedrop to send urlencoded filenames.

As discussed in https://github.com/zulip/zulip/pull/1023, this fix is
already in jquery-filedrop upstream.

Fixes #981.
2016-06-14 15:32:41 -07:00
Tomasz Kolek
4e51a86ea4 Add updownio integration. 2016-06-14 12:14:07 -07:00
Tomasz Kolek
14d69348d3 Add Airbrake integration. 2016-06-13 20:36:40 -07:00
Eklavya Sharma
72ed1f4187 Fix python 3 pyflakes error.
Change raw unicode literal to non-raw.  A raw unicode literal is a
syntax error in python 3.  This error was detected by pyflakes.
2016-06-13 19:44:56 -07:00
Tim Abbott
2219ef7bef bugdown: Rename upload_re to upload_title_re for clarity. 2016-06-13 19:44:56 -07:00
Tim Abbott
6c78036811 Add tests for upload title functionality. 2016-06-13 19:44:56 -07:00
Tim Abbott
5b1cfbc977 bugdown: Fix extraction of titles for uploaded files.
The previous code was associated with a previous version of the upload
URL naming scheme, and thus never triggered in practice.
2016-06-13 19:44:51 -07:00
Michael Cordover
b401ec0af7 Warn on postgres-init-db if >200 messages exist.
Closes #548.
2016-06-13 15:57:51 -07:00
Michael Cordover
53e23743ca Refactor hotkey logic.
Place all hotkey names into a set of three objects in hotkeys.js:

 * hotkeys_shift_insensitive
   These are keys where the behaviour is the same whether they are
   pressed with shift or not.

 * hotkeys_no_modifiers
   These are keys where the event should only be fired when shift
   is not being pressed.

 * hotkeys_shift
   These are keys where the event should only be fired when the key
   is pressed simultaneously with shift.

Each object is a dictionary of key value pairs, with the key being
the keyscan code (e.which) for the key. This is normally the ASCII
key code. The value is an object with two properties, name (which
is the event name) and message_view_only, a boolean. Hotkeys with
message_view_only set to true will not be fired when the home tab
is obscured.
2016-06-13 15:43:49 -07:00
Tim Abbott
0f8d33c488 Fix URL for zulip_main.png for i18n site. 2016-06-13 09:12:52 -07:00
Umair Khan
c8d139b2b1 Fix emoji urls interaction with i18n.
When accessing emojis with relative urls we should start the urls with
a slash so that language code doesn't become part of these urls.

Fixes #1014.
2016-06-13 09:10:11 -07:00
Umair Khan
c1f5ac375c Add linter checks for javascript strings and HTML placeholders.
Checks are added for:
- i18n.t
- placeholder
- compose_error
2016-06-13 09:05:27 -07:00
Umair Khan
6fcfed8d9e Update regexes to capture translation strings.
Strings are captured from:
- i18n.t
- compose_error
- placeholder
2016-06-13 09:03:56 -07:00
Umair Khan
6fb0baaa25 Mark strings translatable.
Following strings are marked translatable:
- All strings which are passed to `button.text` or which affect the
    text of buttons.
- All strings passed to `placeholder`.
- All strings passed to `compose_error`.

Fixes #969
2016-06-13 09:03:56 -07:00
Eklavya Sharma
7ec9cb7e93 Annotate model fields: Attachment.
Also fix clashing annotations.
2016-06-13 20:01:03 +05:30
Eklavya Sharma
86978cb2a3 Annotate model fields: Stream. 2016-06-13 20:01:03 +05:30
Eklavya Sharma
6f5ed6e7c9 Fix annotations clashing with Stream model fields. 2016-06-13 20:01:02 +05:30
Eklavya Sharma
1ec7e124c7 Annotate model fields: Message. 2016-06-13 20:01:02 +05:30
Eklavya Sharma
5a5934a76f Fix annotations clashing with Message model fields. 2016-06-13 20:01:02 +05:30
Eklavya Sharma
f27cff57c3 Annotate model fields: UserProfile. 2016-06-13 20:01:02 +05:30
Eklavya Sharma
71e613424b Fix annotations clashing with UserProfile's model fields. 2016-06-13 20:01:01 +05:30
Eklavya Sharma
9d7a2fdf9d zerver/views/streams.py: Fix annotations.
Mainly fix `str` to `text_type` and use abstract containers.
2016-06-13 20:01:01 +05:30
Eklavya Sharma
9f39c9276f Annotate model fields: DefaultStream, Referral, ScheduledJob. 2016-06-13 19:37:07 +05:30
Eklavya Sharma
4f890cca2a Annotate model fields: UserActivity, UserActivityInterval, UserPresence. 2016-06-13 19:37:07 +05:30
Eklavya Sharma
7290f9cb83 Annotate model fields: Subscription and Huddle. 2016-06-13 19:37:07 +05:30
Eklavya Sharma
ddaaa98b25 Annotate model fields: UserMessage. 2016-06-13 19:30:58 +05:30
Eklavya Sharma
acd1767398 Annotate model fields: Recipient and Client. 2016-06-13 19:30:57 +05:30
Eklavya Sharma
c0004a5874 Annotate model fields: PushDeviceToken and MitUser. 2016-06-13 19:30:57 +05:30
Eklavya Sharma
04740fb620 Annotate model fields: PreregistrationUser. 2016-06-13 19:30:56 +05:30
Eklavya Sharma
d666e00833 Annotate model fields: RealmEmoji and RealmFilter. 2016-06-13 19:30:56 +05:30
Eklavya Sharma
6c7dd07ec2 Annotate model fields: Realm and RealmAlias. 2016-06-13 19:30:56 +05:30
Eklavya Sharma
d169cc5376 zerver/lib/bulk_create.py: Fix string annotations.
Change string type from `str` to `text_type` where required.
Also fix clashing annotations.
2016-06-13 10:08:14 +05:30
Eklavya Sharma
c654c4032d zerver/models.py: Annotate get_display_recipient.
get_display_recipient's annotation clashes with other wrong annotations.
Fix those wrong annotations.
Since get_display_recipient returns a Union, use isinstance checks and
casts to make mypy checks succeed.
2016-06-12 23:34:57 +05:30
Tim Abbott
9f980c64b6 Add detailed LetsEncrypt instructions.
We'll likely want to follow this up with:
(1) Making Let's Encrypt the default instructions for getting a cert.
(2) Moving the "how to get an SSL cert" discussion to its own page.
(3) Eliminating the duplicate content in zulip.org/server.html

But this is definitely an improvement!
2016-06-12 10:35:07 -07:00
Eklavya Sharma
ed61c4c581 Improve model string representation.
Define __str__, __repr__ and __unicode__ correctly on models.
This will help in python 3 compatibility.
2016-06-12 09:55:12 -07:00
Eklavya Sharma
17b9422546 zerver/models.py: Fix regex strings.
Some regex strings were not declared as raw, even though they used
a lot of backslashes.
2016-06-12 09:31:19 -07:00
Eklavya Sharma
81759d56be zerver/models.py: Fix string annotations in UserPresence. 2016-06-12 09:30:53 -07:00
Eklavya Sharma
e11bec28c2 zerver/models.py: Fix annotations (str -> text_type).
Change str to text_type where appropriate in annotations related to
Attachment, PreregistrationUser and Huddle.
2016-06-12 09:30:53 -07:00
Eklavya Sharma
d76bea8f25 zerver/models.py: Identify functions returning QuerySets.
Identify functions which return QuerySets and give them a return type
`Sequence` with appropriate parameter.  Typing them as QuerySet will
not be useful since generic stubs for QuerySets are not available and
not knowing the type of QuerySets is hardly useful for type checking.
2016-06-12 09:30:53 -07:00
Eklavya Sharma
39060aa221 zerver/models.py: Fix annotations related to Message. 2016-06-12 09:30:53 -07:00
Eklavya Sharma
2841aa642d Fix annotations related to make_safe_digest and hashes. 2016-06-12 09:30:53 -07:00
Eklavya Sharma
9020177418 zerver/models.py: Add annotations related to Stream and Client. 2016-06-12 09:30:53 -07:00
Eklavya Sharma
10f2ec043d Fix zerver.lib.utils.generate_random_token.
generate_random_token used to return a value of type six.binary_type
and its return type was annotated as `str`.  This commit fixes that
by making it return a value of type `six.text_type` and updating
the annotation accordingly.
Also fix clashing annnotations.
2016-06-12 09:30:53 -07:00
Eklavya Sharma
56d5785c2e zerver/models.py: Change some constants to unicode literals.
Change choices of UserProfile.avatar_sources and UserProfile.tutorial_status
from str literals to unicode literals.  This is done because these fields
are CharFields, which are of type `six.text_type`.  So the set of values
which they can take should also be of the type `six.text_type`.
Also fix clashing annotations.
2016-06-12 09:30:53 -07:00
Eklavya Sharma
0cc7a6583c zerver/models.py: Fix last_reminder_tzaware annotation.
Fix annotated return type of UserProfile.last_reminder_tzaware from
`str` to `Optional[datetime.datetime]`.
2016-06-12 09:30:33 -07:00
Eklavya Sharma
8acc51218e Fix realm emoji and realm filter annotations.
Change `str` to `text_type` in annotations in zerver/models.py
related to realm emoji and realm filters.
Also fix clashing annotations in zerver/lib/bugdown/__init__.py.
2016-06-12 09:25:42 -07:00
Eklavya Sharma
e68d99eb2e zerver/models.py: Make cache_keys text_type. 2016-06-12 09:25:32 -07:00
Babak
ad895eb690 Annotate zerver/views/report.py. 2016-06-11 18:42:37 -07:00
Vishnu Ks
77ec6217eb Add validation for private message recipients.
The function will reject messages where recipients aren't either a
member of the realm or a member of cross_realm_user_emails.

Fixes: #930.
2016-06-11 11:24:45 -07:00
Vishnu Ks
a717c7df18 Seperate extract PM recipients as function. 2016-06-11 11:22:22 -07:00
Tim Abbott
f44b227b85 Refactor getting cross-realm users into a function. 2016-06-11 11:22:22 -07:00
Eklavya Sharma
f82b28e835 zerver/lib/cache.py: Fix get_cache_backend's annotation. 2016-06-11 09:12:58 -07:00
Eklavya Sharma
0b2d1c30e9 zerver/lib/cache.py: Replace Any with appropriate models.
Due to a cyclic dependency issue, functions having models as parameters
were annotated as Any.
That issue is fixed by importing models inside an `if False:` block,
so that mypy sees them but they are not imported at runtime.
2016-06-11 09:12:58 -07:00
Eklavya Sharma
ff4e95d941 Improve generic_bulk_cached_fetch annotation using TypeVars. 2016-06-11 09:12:42 -07:00
Eklavya Sharma
d27a0e162a zerver/lib/cache.py: update_user_profile_caches return type is None.
In update_user_profile_caches, the return type in annotation was
marked as Any.  Change that to None because, nothing is being returned
in that function.
2016-06-11 09:11:52 -07:00
Eklavya Sharma
53084fe03c Use text_type as type of cache keys and update users.
This changes the type annotations for the cache keys in Zulip to be
consistently text_type, and updates the annotations for values that
are used as cache keys across the codebase.
2016-06-11 09:10:34 -07:00
Eklavya Sharma
d3b80d94a2 Use appropriate string types and correctly encode/decode them. 2016-06-11 17:34:23 +05:30
Eklavya Sharma
f18493f922 Add documentation about zerver/lib/str_utils.py. 2016-06-11 17:31:56 +05:30
Eklavya Sharma
6d29da8cee Add zerver/lib/str_utils.py.
str_utils.py has functions for converting strings from one type to
another.  It also has a TypeVar called NonBinaryStr, which is like AnyStr
except that it doesn't allow bytes.
2016-06-11 16:17:47 +05:30
Tim Abbott
ddd44189a9 Fix coverage reporting on GitHub homepage.
The previous Markdown syntax didn't work correctly on ReadTheDocs, and
now that we have Coveralls reporting backend test coverage as well,
describing our coverage as just "mypy static type" is no longer accurate.

Fixes #964.
2016-06-10 18:48:14 -07:00
Tim Abbott
062287d0b0 Coveralls: Add coverage reporting to backend test suite. 2016-06-10 17:20:00 -07:00
Tim Abbott
8b9c66aac5 Fix missing $ at end of webhook URL patterns. 2016-06-10 17:12:05 -07:00
Vishnu Ks
b926826ea1 Remove default_stream addition and deletion from update_stream_backend.
Default stream addition/removal is done via the /default_stream REST
endpoints.
2016-06-10 16:59:15 -07:00
Eklavya Sharma
85ef5900b4 docs/mypy.md: Add section on annotating strings. 2016-06-10 16:30:14 -07:00
Tim Abbott
c9ef726048 docs/mypy: Add note about Zulip's annotation goals. 2016-06-10 16:30:14 -07:00
Eklavya Sharma
a4c14cd448 Improve docs/mypy.md.
Add link to mypy cheat sheet.
Warn against modifying annotations to somehow make them pass on buggy code.

[Description expanded by tabbott]
2016-06-10 16:30:00 -07:00
Tim Abbott
110d278b86 Fix api/endpoints.html template.
This was apparently broken in the Jinja2 migration.
2016-06-10 15:14:50 -07:00
Tim Abbott
ec8b8cc5c0 Add warning at top of zulip.js about adding new things. 2016-06-10 10:22:24 -07:00
Tim Abbott
7c80456321 Fix and re-enable test_file_upload_authed.
Now that we have a working S3 mock and an effective way to toggle the
upload backend that Zulip is using, we can re-enable this important
end-to-end test of the Zulip S3 upload backend.
2016-06-09 23:07:24 -07:00
rahuldeve
fa13582ffb Serve uploaded files through get_uploaded_file in development.
Previously, uploaded files were served:
* With S3UploadBackend, via get_uploaded_file (redirects to S3)
* With LocalUploadBackend in production, via nginx directly
* With LocalUploadBackend in development, via Django's static file server

This changes that last case to use get_uploaded_file in development,
which is a key step towards being able to do proper access control
authorization.

Does not affect production.
2016-06-09 22:58:25 -07:00
Tim Abbott
719e5487b9 upload: Use classes to define S3/Local upload backends.
This has no functional changes; we just replace the old hacky
assignment of functions with assignment of the upload backend to a
variable.

I'm not totally happy with this, because we end up having to copy the
type annotations of the three methods 4 times each, but this should
make it a lot easier to test the (non-default-in-tests) S3 backend
using end-to-end tests, which would have caught
13bac1cc2a.

I expect we'll iterate on the interface over time; ideally, I'd like
all the code that checks LOCAL_UPLOADS_DIR to be inside upload.py, and
primarily in these classes.
2016-06-09 22:45:03 -07:00
Tim Abbott
13bac1cc2a Fix serving files uploaded to S3.
This was broken by the refactoring to realm_id_str in:
a261a6bbac.
2016-06-09 21:55:50 -07:00
rahuldeve
3e3462da0d Refactor zerver.lib.upload.upload_message_image_through_web_client.
upload_message_image_through_web_client -> upload_message_image_from_request
2016-06-09 21:09:12 -07:00
medullaskyline
39eaf02b40 Annotate zerver.views.webhooks.taiga. 2016-06-09 17:08:55 -07:00
Eklavya Sharma
286d23734a zerver/lib/cache.py: Remove unneeded return statements. 2016-06-09 16:57:11 -07:00
Vishnu Ks
f9f31b79d0 Make default_streams web controllable.
Fixes: #665
2016-06-09 15:24:32 -07:00
Caroline Liu
0c322403a6 Create common test function for select_item_via_typeahead. 2016-06-09 14:55:31 -07:00
Caroline Liu
60e5140406 Add warning for @all / @everyone.
* The warning contains a count of the number of people in the stream.
* An error appears if the warning is ignored and the user tries to
  send the message anyway.
* The message cannot be sent until the warning is acknowledged or @all
  / @everyone is removed.
* This only applies to stream messages and not private messages.

Fixes #853.
2016-06-09 14:38:58 -07:00
Umair Khan
08045241a7 Cache node_modules in Travis.
Fixes: #712
2016-06-09 14:36:22 -07:00
Tim Abbott
584887e588 lint: Require folding of } on same line in else if statements. 2016-06-09 14:05:34 -07:00
Tim Abbott
c35781d505 lint: Require folding of } on same line as else statements. 2016-06-09 14:02:49 -07:00
Tim Abbott
0c1b5006f7 lint: Check for space after if in javascript. 2016-06-09 13:47:12 -07:00
Tim Abbott
eba0d6339f lint: Require space after // in JS comments. 2016-06-09 13:44:24 -07:00
Tim Abbott
a09b950097 lint-all: Check for relative URLs in JSON calls from JS. 2016-06-09 11:59:40 -07:00
Tim Abbott
f9951bb1ca Fix uploading files when using Zulip in another language.
Apparently, relatively URLs in our javascript no longer work.
2016-06-09 11:57:29 -07:00
rahuldeve
ed83bb7f54 Refactor zerver.views.upload. 2016-06-08 09:49:14 -07:00
rahuldeve
e1a2660e70 Refactor realm_id to realm_id_str in url.py. 2016-06-08 09:49:14 -07:00
rahuldeve
23ff717bee Fix annotations: zerver.lib.upload. 2016-06-08 09:49:14 -07:00
Umair Khan
938291a922 Add run-dev.py test to Travis CI test suites.
Fixes #919
2016-06-08 14:10:37 +05:00
Tim Abbott
a829366733 util: Create and use is_pm_recipient helper function. 2016-06-07 21:58:45 -07:00
Tim Abbott
60f6616030 people.js remove: Fix case-sensitive email matching. 2016-06-07 21:58:45 -07:00
Tim Abbott
567c0796f9 Filter: Fix case-sensitive comparisons of email addresses.
Previously, a query containing capital letters might match nothing if
these code paths were ever activated.
2016-06-07 21:58:45 -07:00
Tim Abbott
b25562ca1d Add and use util.is_current_user helper function.
Previously, we were checking if a particular user was the current user
in dozens of places in the codebase, and correct case-insensitive
checks were not used consistently, leading to bugs like #502.
2016-06-07 21:58:44 -07:00
Givorenon
0bf2d171ae Fix fading of messages in dialog and users in side-bar.
Make comparison of emails in compose_fade.js and util.js
case-insensitive to fix fading of users and messages respectively.
2016-06-07 21:31:47 -07:00
Tim Abbott
e14732a12c Remove unused test_beankstalk_message function. 2016-06-07 21:27:29 -07:00
Tomasz Kolek
2ac9c792f3 test_hooks: Use an incoming webhook bot for webhook tests. 2016-06-07 21:27:04 -07:00
Tomasz Kolek
999093b227 Add new is_incoming_webhook bot type.
This type of bot is only able to send messages via webhook endpoints.
2016-06-07 21:23:35 -07:00
Hyunchel Kim
b0702c62fc Annotate zerver.views.messages partially. 2016-06-07 21:09:30 -07:00
Nathan Florea
6bcb6c3192 Removed some unused imports. 2016-06-07 18:13:58 -07:00
Nathan Florea
f11eee8b41 Remove redundant file open.
Calling open() with mode 'w' or 'a' will create a file if it doesn't exist,
while mode 'r' will cause an exception.  This can be easily tested with:
python -c 'open("test.tmp", "w")'
ls test.tmp
2016-06-07 18:10:44 -07:00
Vishnu Ks
d30ea0bc44 Move recent_subjects to stream_data. 2016-06-07 17:50:16 -07:00
Tim Abbott
760ff216ad realm emoji: Add description of the custom emoji feature in settings. 2016-06-07 16:24:07 -07:00
Evan Palmer
bd0fd61821 Annotate zilencer.error_notify.
Also fix the annotation of zilencer.views.report_error.

The `report` arguments are a Dict containing both strings and the
`more_info` sub-dictionary, so we type them as Dict[str, Any].

[tweaked by tabbott]
2016-06-07 13:44:56 -07:00
Conrad Dean
fe2c352ac0 ClientDescriptor: Pass inline sets, not lists, to do_gc_event_queues.
This allows us to more precisely type do_gc_event_queues.
2016-06-07 13:28:45 -07:00
Conrad Dean
d77c70220c send_event: Remove useless return value and annotate.
Detected by mypy.

[tweaked by tabbott to pass mypy check and remove annotations]
2016-06-07 13:27:40 -07:00
Conrad Dean
a4704ba8b2 event_queue: Fix deque values type annotation.
Event IDs in here are ints, as shown by the prune operation.
2016-06-07 13:05:46 -07:00
Tomasz Kolek
29ec2a328b Fix save in topic edit also saving message content edit box.
Fixes #932.
2016-06-07 12:55:58 -07:00
Umair Khan
5becd53414 Add tests for json_error and JsonableError. 2016-06-07 12:41:59 +05:00
Tim Abbott
bc2961d3ac Refactor file upload routes to their own file. 2016-06-06 16:09:05 -07:00
Vishnu Ks
f3a8962612 Replace make_dict() with stream.to_dict(). 2016-06-06 14:46:12 -07:00
gregmccoy
1844f1b054 Add button to exit the Zulip tutorial.
Fixes: #872.

[Tweaked by tabbott to fix focus and rename to "Exit tutorial".]
2016-06-06 14:38:11 -07:00
Tim Abbott
2595ce4a35 Annotate frontend_tests/run-casper. 2016-06-06 13:41:44 -07:00
Tim Abbott
be53c9e39e tests: Check for presence of typing in venv guard.
Since relatively few systems have the typing module, this makes the
checks for whether the user is properly running our test scripts in
the virtualenv more likely to trigger well.
2016-06-06 13:41:34 -07:00
Ernesto Vargas
ffb2f9e84b lint/tests: Give nice error message for common import failures.
This should make users much more likely to be able to debug issues
where they ran Zulip outside the Vagrant environment or virtualenv.

[error messages tweaked by tabbott]
2016-06-06 13:39:26 -07:00
Umair Khan
0ab6b99cbb Fix backend i18n bug.
`makemessages` escapes the `%` sign in `.po` files, but Jinja2 does
not unescape it while replacing the tranlation strings. In Jinja2,
there is an updated implementation of gettext available called
new-style gettext which handles escaping better; this commit switches
to using that.

Fixes #906.
2016-06-06 09:34:51 -07:00
Umair Khan
3092f509b0 Fix Handlebars translations.
Change file mapping between Transifex and Zulip. Fix the typo
in the translation file resource url for Chinese.
2016-06-06 09:30:56 -07:00
Evan Palmer
8afeb7d8ce Annotate webhooks/transifex.py, webhooks/yo.py. 2016-06-05 17:01:53 -07:00
Daw-Ran Liou
7f0709b65c Annotate zerver.views.webhooks.freshdesk
Change the comments into docstrings.
Modified the return type of parse_freshdesk_event to always return a
list of str.
2016-06-05 15:56:27 -07:00
Dalek-Sec
c457f551ea Annotate zerver/views/webhooks/crashlytics.py 2016-06-05 15:54:13 -07:00
medullaskyline
7e30de04ca Annotate zerver.views.webhooks.pingdom. 2016-06-05 15:52:53 -07:00
medullaskyline
4c1da236ad Annotate zerver.views.webhooks.pagerduty. 2016-06-05 15:47:33 -07:00
Daw-Ran Liou
4428287846 Annotate zerver.views.webhooks.stash. 2016-06-05 15:42:25 -07:00
Hyunchel Kim
b79cad0404 Annotate zerver.views.webhooks.teamcity 2016-06-05 15:11:45 -07:00
Hyunchel Kim
f226456675 Add type annotations for analytics/views.py.
Type of parameter for function `is_recent`(line no.812) is `datetime`.
MyPy errors out, however, when the parameter is defined as `datetime`.
To get around, type `Any` is used.
2016-06-05 15:04:24 -07:00
Daw-Ran Liou
26d067fc97 Annotate zerver.views.webhooks.pivotal. 2016-06-05 14:57:53 -07:00
medullaskyline
2369d48a9b Annotate zerver.views.webhooks.newrelic. 2016-06-05 14:52:20 -07:00
Evan Palmer
4bf81b58b4 Annotate zerver/views/webhooks/zendesk.py. 2016-06-05 14:50:52 -07:00
Hyunchel Kim
de34dd1187 Annotate travis webhoook function. 2016-06-05 14:46:26 -07:00
medullaskyline
158914aa98 Annotate zerver.views.webhooks.jira. 2016-06-05 14:36:39 -07:00
Hyunchel Kim
bc87685ea6 bitbucket: Correct return type in annotation. 2016-06-05 14:33:31 -07:00
Daw-Ran Liou
70f44c00b0 check_send_message: Replace args/kwargs with explicit args.
This lets us actually type-checks the various views that are using
check_send_message.
2016-06-05 14:30:38 -07:00
Tim Abbott
553ef81f92 Add lint rules for mypy type annotations. 2016-06-05 13:00:55 -07:00
Max
86fb6467e7 Add annotations to avatar.py, db.py, logging_util.py, unminify.py.
Also, fixed a a small type annotation in users.py because email must
be a string because emails don't support UTF-8 at this time (according
a comment in gravatar_hash in avatar.py).
2016-06-05 12:38:20 -07:00
medullaskyline
2855c285b4 Annotate zerver.forms. 2016-06-05 12:02:19 -07:00
Daw-Ran Liou
90a2dead46 Annotate zerver/views/webhooks/deskdotcom.py. 2016-06-05 11:53:44 -07:00
Deborah Hanus
a261a6bbac Annotate zerver/views/__init__.py.
Also fix typing errors in a few related files.

[with tweaks from tabbott]
2016-06-05 11:34:19 -07:00
Daw-Ran Liou
c9bb93b0d2 Annotate zerver/views/webhooks/beanstalk.py. 2016-06-05 10:54:23 -07:00
Tomasz Kolek
c5b56c15de Fix placement of EDITED in one line status messages.
Fixes #836.
2016-06-05 10:11:05 -07:00
Tim Abbott
15b2dd085e Annotate zerver.lib.test_runner. 2016-06-04 23:23:31 -07:00
Tim Abbott
1ca7c3378b Annotate zerver.lib.testing_mocks.
Also fix some annotations in bugdown to match.
2016-06-04 22:54:49 -07:00
Tim Abbott
157a3efb78 Annotate zerver.exceptions, zerver.filters, zerver.logging_handlers. 2016-06-04 22:51:18 -07:00
Conrad Dean
33dee43179 Annotate zerver/lib/socket.py.
Currently this uses a Union type for connection_id; we need to figure
out what actually sets that and what its type is and fix that later
(see https://github.com/zulip/zulip/issues/896).
2016-06-04 22:36:03 -07:00
Sumana Harihareswara
45b1893284 Convert architecture overview from rST to md for consistency.
Fixes: #668.
2016-06-04 22:27:00 -07:00
Sumana Harihareswara
6f59683324 Fix and clarify architecture overview. 2016-06-04 22:27:00 -07:00
pxhanus
e247d9783f Annotate zilencer/views.py. 2016-06-04 22:07:01 -07:00
Dalek-Sec
2bcf313a85 Added MyPy types to zerver/views/webhooks/codeship.py 2016-06-04 22:03:41 -07:00
acrefoot
7a5bbe040b Fix flaky casper test for messsage edits
.message_edit_notice is too broad of a selector to
test if your most recent edit has posted. Also
check if the currently selected message is the one
that's been edited.
2016-06-04 22:02:18 -07:00
medullaskyline
47c3ec1283 Annotate zerver.lib.upload. 2016-06-04 19:13:03 -07:00
Umair Khan
1bfe566c8d [i18n] Make Json error messages translatable. 2016-06-04 18:48:36 -07:00
Umair Khan
7057b2ae37 Add documentation for json_error and JsonableError.
Add documentation on how to make the strings passing through
json_error and JsonableError translatable.
2016-06-04 18:48:32 -07:00
acrefoot
0f960e04c4 Fix flaky casper test in admin page.
When you deactivate a user, visit another page, and then
come back, the user shows up in another table (the
"Deactivated Users" table), and no longer has the
strikeout styling of a recently deactivated user.

Using the strikeout class as a selector to test if the
user has been reactivated will of course not be a good
test, and if you have a slow machine, lose a
race condition.

When you deactivate a user, visit another page,
and then come back, the user shows up in another
table, and no longer has the strikeout styling
of a recently deactivated user. Using strikeout
class to test if the user has been reactivated
will of course not be a good test, and if you
have a slow machine, lose a race condition.
2016-06-04 18:34:27 -07:00
medullaskyline
c5f0d5b40a Annotate zerver.middleware. 2016-06-04 18:32:06 -07:00
Tim Abbott
9c5f15e89b models: Fix use of non-lazy ugettext at import time.
Was introduced in 03debdf82f.
2016-06-04 17:46:03 -07:00
Daw-Ran Liou
0265968ea2 Annotate zerver/views/user_settings. 2016-06-04 17:41:59 -07:00
James Porter
2a8e8129d1 Make emoji_dump delete old ttx files on start. 2016-06-04 17:05:48 -07:00
James Porter
f5f2d72178 Wrap main bit of emoji_dump.py in a main function. 2016-06-04 17:05:48 -07:00
James Porter
9d58624359 Minor reformatting of emoji_dump.py. 2016-06-04 17:05:48 -07:00
James Porter
d008d96597 Update Emoji set. 2016-06-04 17:05:48 -07:00
Reid Barton
cf93c8bce0 Annotate zerver/views/webhooks/bitbucket.py. 2016-06-04 17:02:59 -07:00
Daw-Ran Liou
8bbd93011d Annotate zerver/views/users.py. 2016-06-04 17:00:53 -07:00
medullaskyline
fcdcccb5df Annotate zerver.lib.digest. 2016-06-04 16:20:18 -07:00
Tim Abbott
d9d0515d3b Add mypy bug number for check_redis type: ignore. 2016-06-04 16:11:18 -07:00
Tim Abbott
f2320bf27f Fix populate_db send_messages annotation. 2016-06-04 16:11:14 -07:00
medullaskyline
7c2c7fb31c Annotate zerver/lib/bulk_create.py. 2016-06-04 15:51:05 -07:00
Tomasz Kolek
8411b2e574 Add Crashlytics integration. 2016-06-04 15:18:42 -07:00
Tomasz Kolek
093e5a96d4 Add Transifex integration.
Fixes: #810.
2016-06-04 14:52:57 -07:00
Taranjeet
c6cfd21bd4 Readme: Fix links for readme.dev and readme.prod.
Absolute links is necessary for readthedocs to properly
link to the files.
2016-06-04 14:15:08 -07:00
Tim Abbott
697125eeae Fix coveralls badge to point to the right branch. 2016-06-04 14:11:21 -07:00
Reid Barton
a0f906edf9 Add mypy coverage status to README.md. 2016-06-04 13:01:19 -07:00
Reid Barton
0b7852f081 Generate mypy coverage report for travis + coveralls. 2016-06-04 13:01:19 -07:00
Max
04e2745136 Annotate debug.py, initial_password.py, narrow.py, response.py.
Also, fixed up the annotations for tornadoviews to better align with
how narrows was defined as `Iterable[Sequence[str]]` rather than
`List[Tuple[str, str]]`.
2016-06-04 12:56:36 -07:00
medullaskyline
7b2db95d02 Annotate zerver/lib/ccache.py. 2016-06-04 12:19:34 -07:00
Tim Abbott
6fba0879a4 Annotate much of the rest of zerver/tests. 2016-06-04 11:53:20 -07:00
Tim Abbott
27e9d3f06b Annotate test_external and test_signup. 2016-06-04 11:53:20 -07:00
Tim Abbott
966375d74c Annotate test_events, test_realm_emoji, test_uploads. 2016-06-04 11:53:20 -07:00
Tim Abbott
f1d58e767b Annotate zerver/tests/test_subs.py.
This required a number of unique-related changes to test_helpers.
2016-06-04 11:53:10 -07:00
Tim Abbott
6f69053911 Annotate most of the rest of bugdown. 2016-06-04 11:35:29 -07:00
Max
c8dc033c3c Annotate camo.py, mandrill_client.py, query.py, session_user.py 2016-06-04 11:28:42 -07:00
Tim Abbott
a1a27b1789 Annotate most Zulip management commands. 2016-06-04 10:12:06 -07:00
Tim Abbott
c2bea0fa08 zulip_finish: Remove useless return statement. 2016-06-04 10:06:31 -07:00
Tim Abbott
ac3989c114 models: Add most missing type annotations. 2016-06-04 00:03:54 -07:00
David Adamec
9e8ea93d3d Add annotations for zerver/lib/validator. 2016-06-03 23:53:49 -07:00
David Adamec
4f3c85a20c Add type annotations to zerver/lib/rest. 2016-06-03 23:48:46 -07:00
Conrad Dean
e7f0698884 Annotate zerver/lib/notifications.py. 2016-06-03 23:45:29 -07:00
Tim Abbott
7fd2956f29 clear_followup_emails_queue: Rename confusing local variable. 2016-06-03 23:45:29 -07:00
medullaskyline
cb84f72f2d Annotate zerver/lib/html_diff.py. 2016-06-03 23:21:26 -07:00
Tim Abbott
2ec0114079 test_bugdown: Add tests for mentions. 2016-06-03 23:18:39 -07:00
Tim Abbott
cfff4f1d49 test_bugdown: Add a bugdown test for alert_words functionality. 2016-06-03 23:18:39 -07:00
Tim Abbott
8c757292cf test_bugdown: Add test for nonmatching realm filters. 2016-06-03 23:18:39 -07:00
Tim Abbott
3a0eb01dda test_messages: Fix huddle test failing when not on Internet. 2016-06-03 23:18:39 -07:00
Tim Abbott
e89730dc8f subject_links: Remove useless RealmFilter.DoesNotExist case. 2016-06-03 23:18:39 -07:00
Tim Abbott
68fba3579d test_bugdown: Add testing of subject_links feature. 2016-06-03 23:18:39 -07:00
gregmccoy
d77e8df3fa Add tests for zerver/views/realm_emoji.py. 2016-06-03 23:12:53 -07:00
Tim Abbott
03debdf82f Fix malformed error message when creating invalid Realm Emoji.
Thanks to Greg McCoy for his help finding this bug.
2016-06-03 23:12:36 -07:00
David Adamec
8ad20e9775 mypy type annotations for zerver/lib/utils 2016-06-03 22:58:15 -07:00
Tim Abbott
a0b332d6cf Add missing dependency decorator of ipython. 2016-06-03 19:29:54 -07:00
Tim Abbott
1552b9308b Fix apnsclient import to match version 0.1.8 used in production.
Apparently, apnsclient moved Connection to a different module between
0.1.8 and 0.2.1.
2016-06-03 19:28:36 -07:00
Tim Abbott
9b86fa96e7 Downgrade python-apnsclient in development to match prod. 2016-06-03 19:27:34 -07:00
Tim Abbott
e7d9b28dfc install-mypy: Pass --upgrade to correctly handle new mypy versions. 2016-06-03 19:11:32 -07:00
Max
0f4673ae3b Add type annotation to mention.py, redis_utils.py, timestamp.py, user_agent.py
Some functions in models.py had input typed as int when they needed to be typed as datetime.datetime
2016-06-03 19:00:16 -07:00
Max
1148f6ff8a Rename timestamp kwarg in to_presence_dict to dt
It is not a timestamp, it is a datetime object. This is better ducktyping
2016-06-03 19:00:16 -07:00
Tim Abbott
5d5f1f46dc Upgrade mypy and typed_ast to latest versions. 2016-06-03 18:59:55 -07:00
Tim Abbott
654bd663aa bugdown: Add annotations for a few more functions.
This resolves the issue with Typeshed #244 by making `upload_re` start
with `ur`.
2016-06-03 18:11:53 -07:00
Tim Abbott
f97b025a33 push_notifications: Fix incorrect Connection import. 2016-06-03 18:11:53 -07:00
medullaskyline
303bd21068 Annotate zserver.lib.push_notifications. 2016-06-03 17:45:54 -07:00
Oren Leaffer
2916fb30cb bugdown: add some type annotations.
Had to add some "type: ignore" because the pattern used in match
doesn't affect the type returned. A fix for this issue has been pushed
to typeshed - https://github.com/python/typeshed/pull/244
2016-06-03 17:03:52 -07:00
medullaskyline
2213a9f41f Annotate zerver/lib/cache_helpers.py 2016-06-03 16:34:30 -07:00
Sumana Harihareswara
8c2382deeb De-duplicate three sections of architecture overview. 2016-06-03 12:56:31 -07:00
Conrad Dean
7f61a5e862 Add type annotations to zerver.lib.test_helpers. 2016-06-03 12:17:26 -07:00
Sumana Harihareswara
4a10923bf1 Add architectural overview documentation.
Fixes: #453.
2016-06-03 11:54:02 -07:00
Max
a6e60419c4 Add types to confirmation/views.py and zerver/tornadoviews.py 2016-06-03 11:26:30 -07:00
Vishnu Ks
4fd569f910 Change add_default_stream method from PATCH to PUT.
This is more consistent with our other routes.
2016-06-03 09:52:51 -07:00
Nathan Florea
85c0da67a4 Changed make_request lambda to more readable function. 2016-06-03 09:17:04 -07:00
Nathan Florea
04c71fadc6 More removal of mutable default arguments.
I've left a few that clearly aren't being passed and aren't being mutated, but
I think I've gotten the rest of them.
2016-06-03 09:17:04 -07:00
Nathan Florea
5fe9076631 Remove some mutable default arguments.
These ones don't fix any bugs, because the mutable arg is never passed
outside of the callable or mutated.  But it's good practice to not use
them in case those invariants are changed in the future.
2016-06-03 09:16:56 -07:00
Nathan Florea
2e4283f60a api: Remove some mutable default arguments.
This could be more succinct with a conditional expression, but this is
easier to read.
2016-06-03 09:16:52 -07:00
Ashish Kumar
9b990e3bd0 Type annotation of zerver/views/alert_words.
[Tweaked by tabbott to annotate the REQ variables the new way]
2016-06-03 08:07:58 -07:00
Ashish Kumar
31bf6b8259 Type annotation of zerver/models.py
[Substantially revised by tabbott]

This probably still has some bugs in it, but having mostly complete
annotations for models.py will help a lot for the annotations folks
are adding to other files.
2016-06-02 23:28:34 -07:00
Tim Abbott
37015fd7c5 Run mypy on zerver/lib/test_auth_backends.py. 2016-06-02 23:01:15 -07:00
Tim Abbott
8cef9675c8 Run mypy on zerver/lib/test_events.py. 2016-06-02 23:00:04 -07:00
Tim Abbott
e6d2b0cdbc Run mypy on zerver/lib/test_unread.py. 2016-06-02 22:59:00 -07:00
Tim Abbott
f3b07ee9aa Run mypy on zerver/lib/test_subs.py. 2016-06-02 22:57:07 -07:00
Tim Abbott
1bdbdd1110 Run mypy on test files where it already passes. 2016-06-02 22:53:28 -07:00
Umair Khan
072551a94e Fix most redownloading in 'vagrant reload --provision'.
Fixes #264.
2016-06-02 22:32:56 -07:00
Oren Leaffer
c2ce5119c6 Annotate zerver.views.tutorial. 2016-06-02 18:49:27 -07:00
Conrad Dean
f8f2f45410 run-mypy: Add --linecount-report and --disallow-untyped-defs flags. 2016-06-02 18:45:52 -07:00
acrefoot
cf15b0b4e6 Fix hanging nc and 'invalid wait time' error on Trusty.
Apparently, 0 isn't a supported wait time value in some versions of nc.
2016-06-02 18:02:13 -07:00
Rachel Kelly
df36216914 Change instances of 'coworkers' to 'users'.
In order to genericize use of Zulip outside companies,
    all instances of coworkers have been changed to users.
    NOTABLE EXCEPTION: When the Zulip instance is domain-
    locked, the reference to coworkers remains.  The reason
    for this is twofold: first, the majority of Zulip instances
    which require a particular domain will be locked to a
    company, and second, the template variable for the domain
    necessary should be added to the alert so it is clear
    to the user what the domain needs to be for access.

Fixes: #861.
2016-06-02 16:05:27 -07:00
Pei-Wei Wu
8d2733ae8c Add mypy type annotations to zerver/views/streams.py. 2016-06-02 15:44:43 -07:00
Ashley Dunn
7826aa7e7f Type annotation of zerver/views/realm_emoji. 2016-06-02 14:01:28 -07:00
John Hampton
d4dbcb80cc README.prod.md: Remove redundant unpacking of the tarball. 2016-06-02 10:32:52 -07:00
Taranjeet
a45bde7822 Docs: Fix typos in docs. 2016-06-02 10:31:41 -07:00
Ashish Kumar
cad342aff6 Correct annotation of generic_bulk_cached_fetch in zerver/lib/cache.py.
Previously, object_ids was tagged as an int, but it is called from
models.py with a string, so we make it an Any.
2016-06-01 14:00:49 -07:00
Tim Abbott
44929523d6 Harden extraction of AUTH_LDAP_BIND_PASSWORD. 2016-05-31 15:06:43 -07:00
Tim Abbott
dac8f7d923 Fix buggy extraction of EMAIL_GATEWAY_PASSWORD.
EMAIL_GATEWAY_PASSWORD has a value of None in the defaults list, so it
will never fail to be in vars().
2016-05-31 15:06:17 -07:00
Tim Abbott
fbc30c2914 test-all: Add a call to run-mypy.
Using mypy to check the codebase is stable and useful enough that it's
worth including it in the default tests run locally.
2016-05-31 15:00:15 -07:00
Tim Abbott
c5b495b775 Update changelog with important changes since last release. 2016-05-31 11:39:47 -07:00
Sumana Harihareswara
cb6ae3a766 Add anchor tags to README.md. 2016-05-31 11:27:21 -07:00
Tim Abbott
d056b5601b docs: Add python-tyoing to RTD dependencies.
This should fix the RTD build having been broken since
d9c4be87d1.
2016-05-31 11:19:49 -07:00
Sumana Harihareswara
337155f280 Remove rST files to replace with Markdown.
Currently (https://github.com/rtfd/recommonmark/issues/3)
the recommonmark bridge (which allows Sphinx to read
Markdown) does not support tables, so the directory structure
doc is now a bulleted list instead of a set of tables.
2016-05-31 10:55:49 -07:00
Sumana Harihareswara
e0bc57ddb1 Rearrange docs table of contents for coherence.
The order and structure of the Read The Docs documentation
was fairly arbitrary; here's a suggestion for how to change
its information architecture so it's more useful to a new
contributor. This adds a short Zulip overview, and a table
of contents and code contribution intro for new contributors.

Fixes: #668.

Signed-off-by: Sumana Harihareswara <sumanah@panix.com>
2016-05-31 09:07:09 -07:00
Sumana Harihareswara
3029b3681f Rearrange sections of testing doc for coherence. 2016-05-31 09:05:38 -07:00
Sumana Harihareswara
f65778f1e2 Improve sub-headings in documentation.
Signed-off-by: Sumana Harihareswara <sumanah@panix.com>
2016-05-31 09:05:38 -07:00
Sumana Harihareswara
86f7695b8c Fix style, formatting in Bugdown docs. 2016-05-31 08:56:58 -07:00
Sumana Harihareswara
ad644afa97 Improve code style guidelines. 2016-05-31 08:56:58 -07:00
Sumana Harihareswara
3631cf3078 Add links in front end build process doc.
Signed-off-by: Sumana Harihareswara <sumanah@panix.com>
2016-05-31 08:56:58 -07:00
Tomasz Kolek
0820ab591a Refresh page when message with /me is edited.
Fixes: #163.

Thanks to Catherine Nguyen (@cnguye11) for her work on this.
2016-05-31 08:29:07 -07:00
Tim Abbott
3ed78eb746 docs/testing: Clean up discussion of rebuilding database. 2016-05-31 07:48:00 -07:00
Christie Koehler
e06492ec3a Improve first-time contributor docs.
Fixes #747. Fixes #748.

This updates README.dev.md to include clear, step-by-step instructions
for setting up the Zulip dev environment Windows 10, OS X El Capitan,
Ubuntu 14.04 Trusty, and Ubuntu 16.04 Xenial. It is aimed at first-time
contributors.

I tested these instructions multiple times on each of the target
systems.

Also added is a "trobleshooting and common errors" section which
documents the most common errors contributors are likely to encounter
during setup and how to fix them.

Improvements based on feedback from @timabbott.

- Fixes whitespace issues so linter will pass.
- Updates memory requirement.
- Re-orders so operating systems are listed alphabetically.
- Updates headings to be clearer.
- Updates and adds ToC entries for clarity.
- Adds screen shot of Zulip dev environment running in browser.
- Adds details about using dev environment, including about logging.
- Misc other minor changes for clarity.
- Adds a stub for docs/logging.md
- Adds details about configuring Cygwin for native symlinks.
2016-05-31 07:47:45 -07:00
Umair Khan
bd4e471706 Check json_error and JsonableError in linter.
Mainly the check is to ensure that all the strings that pass through
these two functions are captured by `makemessages`.
2016-05-31 07:40:42 -07:00
Umair Khan
08fbd57245 [i18n] Make error messages translatable.
Make all strings passing through `json_error` and `JsonableError`
translatable.

Fixes #727
2016-05-31 07:40:42 -07:00
Umair Khan
4b28fcd2f3 Add option to linter to exclude lines from files. 2016-05-31 07:40:42 -07:00
Tim Abbott
ab2d325a08 Update production default streams to be less engineering-centric. 2016-05-31 07:38:25 -07:00
Vishnu Ks
100d885f23 Change default announcement stream to announce.
Fixes #788.
2016-05-31 07:38:07 -07:00
Tim Abbott
960144a49e Desupport using uninstantiated REQ with has_request_variables.
This makes life difficult for doing static type annotations, and
didn't make the code look that much better anyway.
2016-05-31 07:31:15 -07:00
Tim Abbott
41336f3782 lint-all: Check for use of '== None'. 2016-05-31 07:02:04 -07:00
Umair Khan
9a57176ad6 Do shallow testing of backend templates.
Just render the templates without the actual workflow to see if they
don't return a 500 error; this lets us catch various classes of
template bugs automatically.

Fixes #784.
2016-05-31 05:42:17 -07:00
Umair Khan
c884559ec6 Show templates rendered report.
Add two options to the `test-backend` script:

1. verbose
    If given the `test-backend` script will give detailed output.
2. no-shallow
    Default value is False. If given the `test-backend` script will
    fail if it finds a template which is shallow tested.
2016-05-31 16:46:11 +05:00
Umair Khan
daf3d51d4b Send 'template_rendered' signal from Jinja2.
Send the signal only under DEBUG=True just like Django.
2016-05-31 14:31:13 +05:00
Umair Khan
29859c191d Call url correctly from password_reset_email.txt.
In Jinja2 the url function is called differently. We can't give keyword
arguments directly instead we need to pass them through kwargs argument.
2016-05-30 21:05:03 -07:00
Tomasz Kolek
ff4e92dc3d settings: change tab indentation to space. 2016-05-30 20:16:00 -07:00
Greg Price
436499a129 Clean up some relative-path handling in lister using Git magic
This lets us cut out the line which hard-codes how deeply nested in
the tree the `run-mypy` script is, making it simpler to borrow these
scripts in other projects.
2016-05-30 20:01:37 -07:00
Tomasz Kolek
8e144a1f57 Add zip and absoulte_import to pass py3k test. 2016-05-30 22:28:05 +02:00
Tim Abbott
baec0f12cf Add a proper annotation for REQ in streams.py. 2016-05-30 11:41:16 -07:00
Reid Barton
8c6afac7cd Add a stub file for request.py.
This stub file allows us to annotate view functions using the actual
types present in the bodies of the functions, rather than everything
having the type REQ.
2016-05-30 11:28:53 -07:00
Tim Abbott
572c69f3c2 Move REQ and friends to their own module. 2016-05-30 11:24:17 -07:00
Eklavya Sharma
48e7e1a2a1 zerver/lib/actions.py: Rename stream_name to stream.
In function bulk_add_subscriptions, some variables were named
`stream_name` but their type is Stream, not a string.  Rename
those variables to `stream`.
2016-05-30 09:52:59 -07:00
Reid Barton
ff845ebb96 Make tools/install-mypy exit on error. 2016-05-30 09:49:45 -07:00
Tim Abbott
1b59b6826a docs/code-style.rst: Update and clarify shebang rules. 2016-05-29 05:07:50 -07:00
Eklavya Sharma
94e4b39112 Replace python2.7 by python everywhere. 2016-05-29 05:03:08 -07:00
Eklavya Sharma
149938d468 Change shebangs from python2.7 to python. 2016-05-29 05:03:08 -07:00
Eklavya Sharma
1bb6a0db4c Annotate zerver/lib/actions.py. 2016-05-29 04:26:17 -07:00
Eklavya Sharma
2308107805 zerver/lib/actions.py: Use unicode literals.
Convert some strings literals to unicode strings by prefixing with `u`.
2016-05-29 04:26:17 -07:00
Eklavya Sharma
b74f603682 zerver/lib/actions.py: Rename variables and add/edit comments. 2016-05-29 04:26:17 -07:00
Eklavya Sharma
efab224bd1 zerver/lib/actions.py: Remove unneeded return {} statements. 2016-05-29 04:26:17 -07:00
Eklavya Sharma
a2b48f05e5 zerver/lib/actions.py: Fix return values. 2016-05-29 04:26:17 -07:00
Vishnu Ks
dc060248b4 Add instruction to check screenshot of failed tests. 2016-05-28 23:27:24 +05:30
Vishnu Ks
31968f668c Correct frontend_test script path. 2016-05-28 23:23:39 +05:30
Vishnu Ks
fea5ed5b60 Remove unread_subjects from check-all.js
unread_subjects is not used by other modules.
2016-05-27 01:03:21 -07:00
Vishnu Ks
51c86a8e2e Move get_private_message_recipient to message_store.js 2016-05-27 01:03:21 -07:00
Vishnu Ks
89d743787e Move recenter_view to viewport.js. 2016-05-27 01:03:21 -07:00
Vishnu Ks
4be20c4b4a Move scroll_to_selected to navigate.js. 2016-05-27 01:03:21 -07:00
Vishnu Ks
e329c9e0f5 Move maybe_scroll_to_selected to navigate.js. 2016-05-27 01:03:21 -07:00
Vishnu Ks
dc577343fe Move last_viewport_movement_direction to viewport. 2016-05-27 01:03:21 -07:00
Umair Khan
0278ce9102 Move tools/py3_test_reqs.txt to tools/setup/.
Fixes #708
2016-05-26 18:43:24 +05:00
Umair Khan
57f477dd8b Move tools/install-phantomjs to tools/setup/. 2016-05-26 18:28:14 +05:00
Umair Khan
1161862b07 Move tools/emoji_dump to tools/setup/. 2016-05-26 18:28:14 +05:00
Umair Khan
b85526576a Move tools/postgres-init-dev-db to tools/setup/. 2016-05-26 18:28:10 +05:00
Umair Khan
b0991966ab Move tools/postgres-init-test-db to tools/setup/. 2016-05-26 18:28:05 +05:00
Umair Khan
d425e05a02 Move tools/generate-fixtures to tools/setup/. 2016-05-26 17:54:32 +05:00
Umair Khan
8335bd672f Move tools/generate-test-credentials to tools/setup/. 2016-05-26 17:54:28 +05:00
Umair Khan
d5f3a82284 Move tools/download-zxcvbn to tools/setup. 2016-05-26 16:05:23 +05:00
Eklavya Sharma
09400a7e50 End compile-handlebars-templates gracefully.
Catch KeyboardInterrupt from tools/compile-handlebars-templates,
print a message and exit when running in forever mode.
2016-05-25 16:06:35 -07:00
Eklavya Sharma
1ea6171179 Fix an annotation in zerver/lib/cache.py.
This is done to make annotations in zerver/lib/actions.py work correctly.
2016-05-25 15:11:48 -07:00
Eklavya Sharma
d9c4be87d1 Make docs/conf.py pass mypy check. 2016-05-25 15:04:39 -07:00
Eklavya Sharma
30892b2f99 Make makemessages.py pass mypy check. 2016-05-25 15:04:39 -07:00
Eklavya Sharma
ea52fc05ed Make zproject/urls.py pass mypy check. 2016-05-25 15:04:39 -07:00
Eklavya Sharma
1c04560def Re-enable pyflakes in linter and remove python 3 pyflakes errors. 2016-05-25 19:25:13 +05:30
Eklavya Sharma
1a6257394c Make tools/lint-all run on python 3.
Since pyflakes catches some extra errors in python 3, disable
pyflakes for now.
2016-05-25 19:23:13 +05:30
Eklavya Sharma
3185b7e750 Remove unneeded imports from tools/lint-all. 2016-05-25 19:12:09 +05:30
Eklavya Sharma
459c6640bf Fix type annotations in zerver/lib/alert_words.py. 2016-05-24 14:12:11 -07:00
Eklavya Sharma
95d059bfb3 Fix typo in zerver/lib/actions.py. 2016-05-24 14:12:11 -07:00
Eklavya Sharma
c800c87d2d Remove __pycache__ in tools/clean-repo. 2016-05-24 13:43:49 -07:00
Eklavya Sharma
b6bd5445bc End puppet/.../process_fts_updates gracefully.
Catch KeyboardInterrupt in puppet/zulip/files/postgresql/process_fts_updates,
print a message and exit.
2016-05-24 13:28:05 -07:00
Eklavya Sharma
b210727e5c Enable running tools/run-mypy on a subdirectory.
Previously tools/run-mypy could not run on a subdirectory properly
because run-mypy assumed exclude files to be in that subdirectory.
2016-05-24 13:24:15 -07:00
Eklavya Sharma
bd63caed96 Fix exclude startegy in tools/lister.py.
Previously lister.py used to check whether the exclude path is a
substring of a path being considered.  So it would fail when the
exclude path is an absolute path or uses '..' or '.'.
2016-05-24 13:24:15 -07:00
Eklavya Sharma
508a080e08 do_change_bot_type: Add update_fields to user_profile.save(). 2016-05-24 13:21:44 -07:00
Eklavya Sharma
f3e25c68c7 Prevent tools/travis/mypy from failing silently.
Also improve the format of tools/travis/mypy's output.
Also add those files to the exclude list which are failing mypy check.
2016-05-21 12:37:04 +05:30
Umair Khan
ac13187d76 Add translation tags to the templates.
Fixes #726
2016-05-19 22:58:26 -07:00
Umair Khan
82b5d9304b [third] Integrate i18next with Handlebars 2016-05-19 22:58:25 -07:00
Tim Abbott
da69949ccd requirements: Add ipython dependencies. 2016-05-19 22:55:55 -07:00
Tomasz Kolek
8c18b8947f Add bot_type field to UserProfile.
This is intended to support creating different types of bots with
potentially limited permissions.
2016-05-19 22:37:37 -07:00
Paul Traylor
06bc1007fd Add missing \ in CentOS setup instructions. 2016-05-19 19:47:53 -07:00
Tim Abbott
cadbe64265 Fix venv files being included in coverage reports in CI.
e4707af2e2 didn't correctly deal with
the different path for the venv cache in Travis CI.
2016-05-19 10:25:56 -07:00
Tim Abbott
e4707af2e2 test-backend: Fix venv files being included in coverage reports. 2016-05-19 09:36:53 -07:00
Umair Khan
f9bbc5d6ff Enable i18n support in URL configuration.
This supports i18n using all of the following:
- I18N urls
- Session
- Cookie
- HTTP header
2016-05-19 08:33:30 -07:00
Umair Khan
94b2af76f9 Add language mapping for Transifex, Django recognizes zh_CN.
See https://code.djangoproject.com/ticket/18419 for furhter details.
2016-05-19 08:30:56 -07:00
Vishnu Ks
425363ced4 Fix non determinism bug in casper tests.
This manifested as "ReferenceError: Can't find variable: message_list"
on random tests in the Casper test suite.
2016-05-19 08:27:46 -07:00
Tim Abbott
b01196db86 to_log_dict: Add sender_id to logged fields. 2016-05-18 23:02:43 -07:00
Kartik Maji
3d77aa49db Add subscribe button in narrowed view when stream has no messages.
A temporary message appears on successful subscription, with a button
offering to unsubscribe in case the user subscribed by accident.
2016-05-18 20:06:30 -07:00
Kartik Maji
206452c867 Add stream subscription button added narrowed views.
Fixes: #273
2016-05-18 20:06:23 -07:00
Umair Khan
e56d3196ef Update provisioning/release scripts to build .mo files.
We need to update provision.py to compile the messages files, since
they are needed for the new i18n tests.  And of course we need to
include the .mo files in release tarballs; there's a bit of complexity
there around how the tarball archives are created.
2016-05-18 19:30:00 -07:00
Tim Abbott
f35327d148 Add po files imported from Transifex. 2016-05-18 19:03:56 -07:00
Umair Khan
b170b47465 Remove dot from django.po in .tx/config. 2016-05-18 18:59:30 -07:00
Umair Khan
e6d33e8834 jslint: Fix typo. 2016-05-18 18:58:06 -07:00
Aristeidis Fkiaras
3ee210d9e8 Add setting to only allow admins create new streams.
Fixes: #691.

Thanks to Preston Hansen for work on this feature!
2016-05-18 18:53:13 -07:00
Tim Abbott
e781136132 Fix subscribing to existing streams when can_create_streams=False.
Previously, a user with can_create_streams=False would be incorrectly
unable to subscribe to streams, whether the streams previously existed
or not.
2016-05-18 18:47:24 -07:00
Tomasz Kolek
c4254497b2 Add WebhookTestCase abstract class for writing webhook tests.
This cuts a ton of code duplication and semi-duplication between the
webhook tests, and thus should make it a lot easier to write new ones.
2016-05-18 14:37:31 -07:00
Eklavya Sharma
016a2faa23 Make zproject/local_settings.py pass mypy check. 2016-05-18 17:10:18 +05:30
Eklavya Sharma
54759be785 Make zproject/local_settings_template.py pass mypy check. 2016-05-18 17:10:18 +05:30
Eklavya Sharma
70a94a5b23 Expand zproject/ in mypy exclude list. 2016-05-18 17:10:18 +05:30
Eklavya Sharma
6606c30355 Remove zilencer/models.py from mypy exclude list.
zilencer/models.py no longer gives an error on type checking with
mypy.
2016-05-18 17:10:18 +05:30
Eklavya Sharma
7c77522ce4 Make zerver/middleware.py pass mypy check.
This was done by reporting python/mypy#1540 and upgrading to the
latest version of mypy which has the fix for this.
2016-05-18 17:10:18 +05:30
Eklavya Sharma
98afe000ee Make zerver/lib/statistics.py pass mypy check. 2016-05-18 17:10:18 +05:30
Eklavya Sharma
0dcd8b387d Make zerver/lib/bugdown/fenced_code.py pass mypy check. 2016-05-18 17:10:17 +05:30
Eklavya Sharma
3441f0848c Annotate pg_backup_and_purge.py. 2016-05-18 17:10:17 +05:30
Eklavya Sharma
66bb6394e5 Make api/zulip/__init__.py pass mypy check. 2016-05-18 17:10:17 +05:30
Eklavya Sharma
46757f07bf Make zerver/lib/actions.py pass mypy check. 2016-05-18 17:10:17 +05:30
Eklavya Sharma
16067b7013 Make zerver/views/webhooks/jira.py pass mypy check. 2016-05-18 17:10:17 +05:30
Sumana Harihareswara
5f053e0047 Add remaining codebases to README.md.
The README should link to all the Zulip codebases. Also, it makes
sense to split up app code from integration and other glue code.

Fixes: #674.
2016-05-17 16:56:46 -07:00
Sumana Harihareswara
3d267a5438 Add doc-building dependencies to requirements.txt.
Fixes: #794.
2016-05-17 12:21:25 -07:00
Tim Abbott
b38913c8f9 Upgrade sphinx version used in ReadTheDocs.
According to https://github.com/spatialaudio/nbsphinx/issues/26, this
should fix support for mixing rST and markdown in our docs.

Fixes #668.
2016-05-14 14:27:21 -07:00
Sumana Harihareswara
34d2c505e9 Fix typos in README.md. 2016-05-13 22:18:20 -07:00
Tim Abbott
c3985520e5 webhooks: Remove unnecessary get_client imports. 2016-05-13 12:25:12 -07:00
Tomasz Kolek
db7ea8b484 Move getting client to api_key_only_webhook_view.
This decreases the amount of convention developers need to understand
in order to write a new webhook integration.
2016-05-13 12:22:38 -07:00
Tim Abbott
d55c3bd142 Add npm-debug.log to gitignore. 2016-05-12 16:32:51 -07:00
Tim Abbott
12b32d3889 check-py3: Display git status --porcelain output. 2016-05-12 16:30:58 -07:00
Tim Abbott
aa7ff158b6 Reduce development environment RAM requirements by 750MB.
Since we merged cd2348e9ae more than a
month ago and haven't seen any noticable regresions as a result, it's
reasonable at this point to do a corresponding decrease in our
documented RAM requirements for the Zulip development environment.
2016-05-12 16:12:43 -07:00
Tim Abbott
e7cb1e3f92 README.dev: Make 'official Zulip PPA' link to the PPA. 2016-05-12 15:38:02 -07:00
Tim Abbott
efd24b374e analytics: Fix cnts variable reuse with different type.
Found using mypy.
2016-05-12 14:07:32 -07:00
Sumana Harihareswara
038c1ea20f Add integration project links to README.
The README should link to all Zulip-specific open source projects
so users know where to file bugs and code contributors can pitch
in.

Fixes: #674.
2016-05-12 13:45:20 -07:00
Tomasz Kolek
f486e47d4d Change CircleCI logo on /integrations to leave off the name.
The name is already present in the label just below.
2016-05-12 13:14:18 -07:00
Umair Khan
99b73c2728 digest emails: Fix typo in remaining_unread_pms_count.
This error likely caused this feature to never trigger.
2016-05-12 13:09:37 -07:00
Umair Khan
dfc58b0ed0 Upgrade digest email templates to Jinja2.
Fixes: #780
2016-05-13 01:01:28 +05:00
Tim Abbott
60722a8fce Integration guide: Add a note about spelling integraiton names. 2016-05-11 21:19:15 -07:00
Tomasz Kolek
eeeb4d0c92 Add CircleCI integration.
Fixes: #617.
2016-05-11 21:17:37 -07:00
Tom
014fcf7570 README.dev.md: Add PPA instructions to get tsearch-extras.
Fixes #109.
2016-05-11 16:22:53 -07:00
Sumana Harihareswara
c270b94b21 Fix formatting, grammar, and style in Yo integration docs. 2016-05-10 21:34:05 -07:00
Sumana Harihareswara
4c529cdf37 Update Getting Started With Hubot doc link. 2016-05-10 20:54:28 -04:00
Bert Muthalaly
261dac25a5 Fix skipping to latest messages (fast_forward_pointer).
The refactoring in b8ec8f5ef0 had a
slightly wrong URL.
2016-05-10 17:24:42 -07:00
Tim Abbott
2409ac9b2f cache: Add type annotations to active_*_dict_fields. 2016-05-10 11:48:03 -07:00
Tim Abbott
f2aee961e1 test_auth_backends: Fix unused variables. 2016-05-10 11:46:39 -07:00
Tim Abbott
92bec8cfea Merge Zulip 1.3.12 security release. 2016-05-10 11:32:26 -07:00
Tim Abbott
90634356cb Add changelog for Zulip 1.3.12 release. 2016-05-10 09:51:49 -07:00
Tim Abbott
9b65464b6b logout_all_users: Add option to logout deactivated users. 2016-05-10 09:50:57 -07:00
Tim Abbott
393159bbd8 queue: Disable RabbitMQ heartbeat in BlockingConnection.
Fixes #741.
2016-05-10 09:50:57 -07:00
Tim Abbott
6f282581f7 requirements: Upgrade pika to version 0.10.0.
This is needed to fix RabbitMQ heartbeat issues that cause connections
to drop (#741).  It's also relevant for Python 3 support.
2016-05-10 09:50:57 -07:00
Tim Abbott
d82e44ecd0 queue: Refactor Pika credentials code to be a bit cleaner. 2016-05-10 09:50:57 -07:00
Tim Abbott
620debc5fd Change PrincipalError to return status code 403 by default. 2016-05-10 09:50:57 -07:00
Tim Abbott
85c64c9f93 zulip_login_required: Add checks for active users and realms.
Like the recent change blocking JSON endpoints for deactivated users
and users in deactivated realms, this change is a hardening
improvement.  Those users should be unable to get an active session
anyway, but if somehow one is leaked, this means they won't be able to
access any user data.
2016-05-10 09:50:57 -07:00
Tim Abbott
be216506a9 Improve api_fetch_api_key error messages.
Previously, api_fetch_api_key would not give clear error messages if
password auth was disabled or the user's realm had been deactivated;
additionally, the account disabled error stopped triggering when we
moved the active account check into the auth decorators.
2016-05-10 09:50:57 -07:00
Tim Abbott
52ddd500f0 Add tests for authentication backends. 2016-05-10 09:50:57 -07:00
Tim Abbott
38c82083de Add test suite for deactivated users. 2016-05-10 09:50:57 -07:00
Tim Abbott
df7466e893 Add test suite for deactivate realms. 2016-05-10 09:50:57 -07:00
Tim Abbott
76814f37a3 decorators: Block access to JSON endpoints for deactivated users.
While in theory users should be unable to get a valid session in order
to access these endpoints in the first place, this provides an extra
layer of hardering to prevent a deactivated user with a session from
accessing data via the old-style JSON API.
2016-05-10 09:50:57 -07:00
Tim Abbott
b28b3cd65c CVE-2016-4427: Fix access by deactivated realms/users.
The security model for deactivated users (and users in deactivated
realms) being unable to access the service is intended to work via two
mechanisms:

* All active user sessions are deleted, and all login code paths
  (where a user could get a new session) check whether the user (or
  realm) is inactive before authorizing the request, preventing the
  user from accessing the website and AJAX endpoints.
* All API code paths (which don't require a session) check whether the
  user (and realm) are active.

However, this security model was not implemented correctly.  In
particular, the check for whether a user has an active account in the
login process was done inside the login form's validators, which meant
that authentication mechanisms that did not use the login form
(e.g. Google and REMOTE_USER auth) could succeed in granting a session
even with an inactive account.  The Zulip homepage would still fail to
load because the code for / includes an API call to Tornado authorized
by the user's token that would fail, but this mechanism could allow an
inactive user to access realm data or users to access data in a
deactivated realm.

This fixes the issue by adding explicit checks for inactive users and
inactive realms in all authentication backends (even those that were
already protected by the login form validator).

Mirror dummy users are already inactive, so we can remove the explicit
code around mirror dummy users.

The following commits add a complete set of tests for Zulip's inactive
user and realm security model.
2016-05-10 09:50:48 -07:00
Tim Abbott
b31ac1eca9 Fix users in deactivated realms sending webhook messages.
In a deactivated realm, webhooks would still successfully send
messages, since there was no check for whether the realm was active in
api_key_only_webhook_view.
2016-05-10 09:50:48 -07:00
Tim Abbott
9da73b22d3 assert_json_error_contains: Support passing a status code.
Previously this test helper function hardcoded 400.
2016-05-10 09:50:48 -07:00
Tim Abbott
3cde06ea33 Add support for setting HTTP status codes in JsonableError. 2016-05-10 09:50:48 -07:00
Tim Abbott
b38c50c6bb docs: Document possible auditing features in security model. 2016-05-10 09:50:47 -07:00
Tim Abbott
44fae09a48 docs: Clarify security model around bots and invite-only streams. 2016-05-10 09:50:47 -07:00
Tim Abbott
b4ccca300b Add tests for whether API keys appear in initial state data. 2016-05-10 09:50:47 -07:00
Tim Abbott
07fc47f953 CVE-2016-4426: Fix non-admin users having access to all bot API keys.
Long ago, there was work on an experimental integration model where
every user in a realm would have administrative control over all bots,
with the goal of simplifying the process of setting up communally
administered bots for smaller teams.  While that new model was never
fully implemented (and thus never setup as an option), an error in
that original implementation meant that the data on all bots in a
realm, including their API keys, was sent to the browsers of users via
the `realm_bots` variable in `page_params`.  The data wasn't displayed
in the UI for non-admin users, but was available via e.g. the
javascript console.

This commit updates this behavior to only send sensitive bot data like
API keys to the owner of the bot (and realm admins).

We may in the future implement a model simplifying communally
administered integrations, but if we do that, those bots should be
limited in their capabilities (e.g. only able to send webhook
messages).

This bug has been present since Zulip was released as open source.
2016-05-10 09:50:02 -07:00
Tim Abbott
b869be9301 style: Use 'not in' consistently rather than not foo in. 2016-05-09 17:00:10 -07:00
Tim Abbott
9cf18f8535 settings: Fix whitespace errors. 2016-05-09 16:53:12 -07:00
Tim Abbott
624258750c confirmation: Fix trailing whitespace. 2016-05-09 16:49:33 -07:00
Tim Abbott
43f167849b queue: Disable RabbitMQ heartbeat in BlockingConnection.
Fixes #741.
2016-05-09 10:23:28 -07:00
Tim Abbott
2dfa7562e2 requirements: Upgrade pika to version 0.10.0.
This is needed to fix RabbitMQ heartbeat issues that cause connections
to drop (#741).  It's also relevant for Python 3 support.
2016-05-09 10:23:28 -07:00
Tim Abbott
0c42fc2f8f queue: Refactor Pika credentials code to be a bit cleaner. 2016-05-09 10:23:28 -07:00
Tim Abbott
0161d2fddd Cleanup guardian-based complexity in get_realm_user_dicts.
The old code for this lookup was unnecessarily complicated because we
were working around Guardian, where the `is_realm_admin` check was
extremely expensive.
2016-05-09 10:12:35 -07:00
Tim Abbott
2a2cbd60c3 cache: Fix fragile active_bot_dicts_in_realm caching model.
The issue here is similar to that in the previous commit.
2016-05-09 10:12:35 -07:00
Tim Abbott
fbc7e977ac cache: Fix fragile active_user_dicts_in_realm caching model.
Previously we relied on having two matching list of fields for the
get_active_user_dicts_in_realm, one in the actual code and the other
in the caching system.  By unifying these lists to have a single
source, we eliminate a class of caching bugs we might otherwise
regularly introduce.
2016-05-09 10:12:35 -07:00
Tim Abbott
f02571202a EventsRegisterTest: display full error diffs. 2016-05-09 10:12:35 -07:00
Tim Abbott
6c744564a7 travis: Add debugging code for rabbitmq nagios failures. 2016-05-09 09:55:18 -07:00
Umair Khan
0d324925b5 Add documentation on translation tags.
[substantially modified by tabbott]
2016-05-09 09:55:18 -07:00
Umair Khan
5359e6b0d4 Convert Zulip to use Jinja2 templates.
This results in a substantial performance improvement for all of
Zulip's backend templates.

Changes in templates:
- Change `block.super` to `super()`.
- Remove `load` tag because Jinja2 doesn't support it.
- Use `minified_js()|safe` instead of `{% minified_js %}`.
- Use `compressed_css()|safe` instead of `{% compressed_css %}`.
- `forloop.first` -> `loop.first`.
- Use `{{ csrf_input }}` instead of `{% csrf_token %}`.
- Use `{# ... #}` instead of `{% comment %}`.
- Use `url()` instead of `{% url %}`.
- Use `_()` instead of `{% trans %}` because in Jinja `trans` is a block tag.
- Use `{% trans %}` instead of `{% blocktrans %}`.
- Use `{% raw %}` instead of `{% verbatim %}`.

Changes in tools:
- Check for `trans` block in `check-templates` instead of `blocktrans`

Changes in backend:
- Create custom `render_to_response` function which takes `request` objects
  instead of `RequestContext` object. There are two reasons to do this:
    1. `RequestContext` is not compatible with Jinja2
    2. `RequestContext` in `render_to_response` is deprecated.
- Add Jinja2 related support files in zproject/jinja2 directory. It
  includes a custom backend and a template renderer, compressors for js
  and css and Jinja2 environment handler.
- Enable `slugify` and `pluralize` filters in Jinja2 environment.

Fixes #620.
2016-05-09 09:55:18 -07:00
Umair Khan
cec0530fd8 analytics: Fix reference to potentially null row.hours_per_user. 2016-05-09 09:54:39 -07:00
Umair Khan
f20b907f96 base.html: Add check for undefined user_profile.
The previous code that neglected this check worked with the Django
templating engine but will not work with Jinja2.
2016-05-09 09:48:37 -07:00
Tim Abbott
804dad42e6 travis: Run various Nagios checks in production tests. 2016-05-08 17:35:50 -07:00
Tim Abbott
00ccf147cd check_nagios_send_time: Add --insecure option for use in tests. 2016-05-08 17:35:50 -07:00
Tim Abbott
744e8ad0e3 travis: Set prod EXTERNAL_HOST to resolve correctly.
This is needed to use check_send_receive_time in the tests.
2016-05-08 17:35:50 -07:00
Tim Abbott
e4c098fba4 travis: Verify all supervisord jobs are running in production test.
This requires a bit of complexity since supervisord automatically
restarts failing jobs.
2016-05-08 17:35:50 -07:00
Tim Abbott
40de75d9e6 travis: Verify the server doesn't 500 in production test. 2016-05-08 17:35:50 -07:00
Tim Abbott
c0d38f42f1 supervisor: Use 127.0.0.1 rather than localhost for tornado.
In theory these should be the same, but in misconfigured environments
(such at Travis CI) where /etc/hosts has multiple entries for
"localhost", 127.0.0.1 is safer than "localhost".
2016-05-08 17:35:50 -07:00
Vladislav Manchev
52e96915e2 check-templates: Allow HTML tag brackets inside tag attributes. 2016-05-08 16:33:03 -07:00
Preston Hansen
635828069f Add feature to mark all in stream/topic as read with mouse.
Fixes #736.
2016-05-08 09:02:46 -07:00
Tim Abbott
34fb276b7b changelog: Add some items since the last release. 2016-05-07 20:34:14 -07:00
Tim Abbott
c5a44043a8 Cleanup changelog.md to be better documentation. 2016-05-07 20:34:06 -07:00
Tim Abbott
1c24cb32a5 Move changelog.md to docs/ and add to ReadTheDocs. 2016-05-07 20:26:44 -07:00
Tim Abbott
e5e133eccc Update docs to reflect the elimination of /bin. 2016-05-07 19:37:06 -07:00
Tim Abbott
6e1872987d Move bin/get-django-setting to scripts/. 2016-05-07 19:37:06 -07:00
Tim Abbott
a315849a9e Move bin/log-management-command to scripts/lib/.
We're in the process of eliminating the bin/ subdirectory in favor of
the scripts/ tree, and this one isn't user-facing.
2016-05-07 19:37:06 -07:00
Tim Abbott
cb81a59e38 Move write-rabbitmq-consumers-state-file to scripts/nagios/. 2016-05-07 19:37:06 -07:00
Tim Abbott
2761c012e5 Move rabbitmq consumer checks from bots/ to scripts/nagios/. 2016-05-07 19:37:06 -07:00
Tim Abbott
be6566dc5c nagios: Move cron_file_helper from bots/ to scripts/lib.
This ensures the tool is available in Zulip production deployments.
2016-05-07 19:37:06 -07:00
Tim Abbott
73b3f7a26e settings: Fix new-style template LOADERS syntax. 2016-05-07 19:35:16 -07:00
Tim Abbott
4c7b4fcea1 requirements: Downgrade Jinja2 and MarkupSafe to match Trusty apt.
The moto-based tests still pass with the older version, and this will
safe some trouble when we migrate Zulip to the Jinja2 templating system.
2016-05-07 17:38:26 -07:00
Tim Abbott
d20791eb6a docs/new-feature-tutorial: Fix typos in templates path. 2016-05-07 17:37:01 -07:00
Umair Khan
6a0c7fec72 analytics: Add at_risk_count to Totals row in realm summary.
This fixes reading from an unset value in realm_summary_table, which
is fine with the Django template engine but will be problematic with
jinja2.
2016-05-07 17:30:06 -07:00
Umair Khan
4620cd8483 settings: Migrate template settings to using TEMPLATES.
This is cleaner and also is necessary preparation for supporting
Jinja2.
2016-05-07 17:29:51 -07:00
Umair Khan
a3acd5e8e9 settings: Add LocaleMiddleware to MIDDLEWARE_CLASSES.
This is needed for internationalization.
2016-05-07 17:00:10 -07:00
Tim Abbott
c1c1be1d36 Add Transifex config file and Transifex client dependency.
This makes it easy to download translations from Transifex.
2016-05-07 16:59:59 -07:00
Tim Abbott
5efb38f093 Add compiled translation messages files (.mo) to gitignore. 2016-05-07 16:59:45 -07:00
Tim Abbott
bfdde2e9b9 provision: Add missing dependency on python3-dev.
This fixes an issue introduced by
b21454d05e, where the typed_ast module
wouldn't build properly when provisioning, because we didn't have the
necessary headers installed.
2016-05-07 13:31:26 -07:00
Tim Abbott
6139e8948a travis: User REQ framework for extracting JSON payload. 2016-05-07 11:54:14 -07:00
Tim Abbott
678adc2048 webhooks: Use REQ more consistently in stream name parsing.
To avoid the potential for introducing regressions here, we carefully
pass a default to REQ or not based on how the existing webhook's
parsing code worked.  In the longer term, we'll want to make the
behavior consistent.
2016-05-07 11:54:14 -07:00
Tomasz Kolek
c2de38239e Add payload validation to has_request_variables and REQ tasks.
[with tweaks by tabbott]
2016-05-07 11:54:09 -07:00
Tim Abbott
c1a680e2a9 rate_limiter: Fix misplaced type annotation and cleanup code.
You don't put type annotations on return values.
2016-05-06 13:38:12 -07:00
Tim Abbott
3a0e7c217f mypy: Move verbose error output to travis CI wrapper.
This makes it easier to pipe the output of tools/run-mypy to tools
like grep.
2016-05-06 13:38:12 -07:00
Tim Abbott
b21454d05e mypy: Use the new --fast-parser option.
This makes mypy about 15% faster running on the Zulip codebase (from
7s=>6s on my laptop), which seems worth it for losing a couple files.

This option requires a new dependency, which we add to the
mypy-specific requirements.txt file.
2016-05-06 13:38:12 -07:00
Tim Abbott
1807e855e7 Upgrade mypy to the new 0.4.0 release.
Also update the mypy command line to not use deprecated argument names.

This introduces a few errors, so we exclude the relevant files to keep
the mypy output clean.
2016-05-06 13:38:12 -07:00
Tim Abbott
4219a6779f socket: Initialize client_id to None by default.
This fixes an exception where client_id was never set in an error code
path.  It shouldn't be needed, but I think this makes the code clearer
and this will help in debugging the actual problem.

Related to #753.
2016-05-05 14:49:26 -07:00
Tim Abbott
542af0d6b6 lint: Add option to print verbose timing output. 2016-05-04 14:22:52 -07:00
Tim Abbott
e2aeee0c35 lint: Add check for unnecessary whitespace between % and (. 2016-05-04 14:22:52 -07:00
Tim Abbott
191201bd10 Fix unnecessary whitespace between % and (. 2016-05-04 14:22:52 -07:00
Tim Abbott
72ee9f5137 lint: Check for unnecssary whitespace after ','s. 2016-05-04 14:17:27 -07:00
Tim Abbott
54022ac204 Fix unnecessary whitespace between , and ). 2016-05-04 14:16:53 -07:00
Tim Abbott
dd40f51fee Add code to clean the venv cache of old venvs. 2016-05-03 15:04:03 -07:00
Tim Abbott
6a335fc090 Travis: Use /srv/zulip-venv-cache cache. 2016-05-03 14:48:21 -07:00
Tim Abbott
9970341ede Fix caching of install-phantomjs library in Travis CI. 2016-05-03 14:48:21 -07:00
Tim Abbott
ae6037668a test_runner: Add debugging tips for missing test modules. 2016-05-03 12:33:47 -07:00
Tim Abbott
bab267f332 test_runner: Improve error handling when importing test files.
The error message for a test file that doesn't import properly was
previously pretty difficult to understand and it wasn't clear how to
debug the issue.
2016-05-03 12:17:10 -07:00
Tim Abbott
9f786a5131 provision: Add caching of virtualenvs by sha1sum of requirements.txt.
This should save a couple minutes on the time it takes to provision a
Zulip environment on a machine that has provisioned with the same
requirements.txt file content before.

We can't yet use this in Travis CI because Travis CI doesn't support
using both sudo and caching in the same build.
2016-05-02 23:08:59 -07:00
Tim Abbott
ef95917da5 provision: Refactor virtualenv creation into a function. 2016-05-02 22:35:18 -07:00
rahuldeve
899bfb97ee Add tests for managing uploads in S3. 2016-05-02 22:14:47 -07:00
rahuldeve
01fb6c77a2 Add test for managing files in Local Storage. 2016-05-02 22:14:47 -07:00
rahuldeve
c9b242b5d1 Modify requirements.txt for adding moto package and its dependencies. 2016-05-02 22:14:47 -07:00
rahuldeve
dde832b158 Add Attachment model to keep track of uploads.
This commit adds the capability to keep track and remove uploaded
files.  Unclaimed attachments are files that have been uploaded to the
server but are not referred in any messages.  A management command to
remove old unclaimed files after a week is also included.

Tests for getting the file referred in messages are also included.
2016-05-02 22:14:47 -07:00
Tim Abbott
391a225595 lint: Check for missing space after comments. 2016-05-02 22:10:47 -07:00
Tim Abbott
762a3188ee Fix missing whitespace after # in comments. 2016-05-02 22:10:47 -07:00
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
843 changed files with 51426 additions and 10841 deletions

2
.coveralls.yml Normal file
View File

@@ -0,0 +1,2 @@
service_name: travis-pro
repo_token: hnXUEBKsORKHc8xIENGs9JjktlTb2HKlG

3
.gitattributes vendored
View File

@@ -14,7 +14,8 @@
/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
/locale export-ignore

11
.gitignore vendored
View File

@@ -13,6 +13,7 @@
/update-prod-static.log
frontend_tests/casper_tests/server.log
frontend_tests/casper_lib/test_credentials.js
memcached_prefix
/prod-static
/errors/*
*.sw[po]
@@ -24,7 +25,7 @@ zerver/fixtures/migration-status
zerver/fixtures/test_data1.json
.kdev4
zulip.kdev4
memcached_prefix
remote_cache_prefix
coverage/
/queue_error
.test-js-with-node.html
@@ -37,7 +38,11 @@ event_queues.json
static/js/bundle.js
static/third/gemoji/
static/third/zxcvbn/
tools/emoji_dump/bitmaps/
tools/emoji_dump/*.ttx
tools/setup/emoji_dump/bitmaps/
tools/setup/emoji_dump/*.ttx
tools/phantomjs
node_modules
npm-debug.log
uploads/
test_uploads/
*.mo

View File

@@ -1,19 +1,33 @@
before_install:
- nvm install 0.10
install:
- pip install coveralls
- tools/travis/setup-$TEST_SUITE
- tools/clean-venv-cache --travis
cache:
- apt: false
- directories:
- /srv/phantomjs
- $HOME/phantomjs
- $HOME/zulip-venv-cache
- node_modules
- $HOME/node
env:
- TEST_SUITE=frontend
- TEST_SUITE=backend
- TEST_SUITE=production
- TEST_SUITE=py3k
global:
- COVERALLS_PARALLEL=true
- COVERALLS_SERVICE_NAME=travis-pro
- COVERALLS_REPO_TOKEN=hnXUEBKsORKHc8xIENGs9JjktlTb2HKlG
matrix:
- TEST_SUITE=frontend
- TEST_SUITE=backend
- TEST_SUITE=production
- TEST_SUITE=py3k
language: python
python:
- "2.7"
matrix:
include:
- python: "3.4"
env: TEST_SUITE=mypy
# command to run tests
script:
- ./tools/travis/$TEST_SUITE
@@ -22,3 +36,7 @@ services:
- docker
addons:
postgresql: "9.3"
after_success:
coveralls
notifications:
webhooks: https://coveralls.io/webhook?repo_token=$COVERALLS_REPO_TOKEN

17
.tx/config Normal file
View File

@@ -0,0 +1,17 @@
[main]
host = https://www.transifex.com
[zulip.djangopo]
source_file = locale/en/LC_MESSAGES/django.po
source_lang = en
type = PO
file_filter = locale/<lang>/LC_MESSAGES/django.po
lang_map = zh-Hans: zh_CN
[zulip.translationsjson]
source_file = static/locale/en/translations.json
source_lang = en
type = KEYVALUEJSON
file_filter = static/locale/<lang>/translations.json
lang_map = zh-Hans: zh-CN

View File

@@ -9,4 +9,7 @@ RUN apt-get update && apt-get install -y \
RUN useradd -d /home/zulip -m zulip && echo 'zulip ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
USER zulip
RUN ln -nsf /srv/zulip ~/zulip
WORKDIR /srv/zulip

File diff suppressed because it is too large Load Diff

158
README.md
View File

@@ -1,5 +1,11 @@
Zulip
=====
**[Zulip overview](#zulip-overview)** |
**[Installing for dev](#installing-the-zulip-development-environment)** |
**[Installing for production](#running-zulip-in-production)** |
**[Ways to contribute](#ways-to-contribute)** |
**[How to get involved](#how-to-get-involved-with-contributing-to-zulip)** |
**[License](#license)**
# Zulip overview
Zulip is a powerful, open source group chat application. Written in
Python and using the Django framework, Zulip supports both private
@@ -12,85 +18,128 @@ 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.
Installing the Zulip Development environment
============================================
[![Build Status](https://travis-ci.org/zulip/zulip.svg?branch=master)](https://travis-ci.org/zulip/zulip) [![Coverage Status](https://coveralls.io/repos/github/zulip/zulip/badge.svg?branch=master)](https://coveralls.io/github/zulip/zulip?branch=master)
The Zulip development environment is the recommened option for folks
## Installing the Zulip Development environment
The Zulip development environment is the recommended option for folks
interested in trying out Zulip. This is documented in
[README.dev.md](README.dev.md).
[README.dev.md](https://github.com/zulip/zulip/blob/master/README.dev.md).
Running Zulip in production
===========================
## Running Zulip in production
Zulip in production only supports Ubuntu 14.04 right now, but work is
ongoing on adding support for additional platforms. The installation
process is documented in https://zulip.org/server.html and in more
detail in [README.prod.md](README.prod.md).
process is documented at https://zulip.org/server.html and in more
detail in [README.prod.md](https://github.com/zulip/zulip/blob/master/README.prod.md).
Contributing to Zulip
=====================
## Ways to contribute
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]. You can also improve
[Zulip.org][z-org].
* **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
repositories.
* **App codebases**. This repository is for the Zulip server and web
app (including most integrations); the [desktop][], [Android][], and
[iOS][] apps, are separate repositories, as is [our experimental React
Native iOS app][ios-exp].
How to get involved with contributing to Zulip
==============================================
* **Glue code**. We maintain a [Hubot adapter][hubot-adapter] and several
integrations ([Phabricator][phab], [Jenkins][], [Puppet][], [Redmine][],
and [Trello][]), plus [node.js API bindings][node], and a [full-text search
PostgreSQL extension][tsearch], as separate repos.
First, subscribe to the Zulip [development discussion mailing list](https://groups.google.com/forum/#!forum/zulip-devel).
* **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
[ios-exp]: https://github.com/zulip/zulip-mobile
[email-android]: https://groups.google.com/forum/#!forum/zulip-android
[email-ios]: https://groups.google.com/forum/#!forum/zulip-ios
[hubot-adapter]: https://github.com/zulip/hubot-zulip
[jenkins]: https://github.com/zulip/zulip-jenkins-plugin
[node]: https://github.com/zulip/zulip-node
[phab]: https://github.com/zulip/phabricator-to-zulip
[puppet]: https://github.com/matthewbarr/puppet-zulip
[redmine]: https://github.com/zulip/zulip-redmine-plugin
[trello]: https://github.com/zulip/trello-to-zulip
[tsearch]: https://github.com/zulip/tsearch_extras
[transifex]: https://www.transifex.com/zulip/zulip/
[z-org]: https://github.com/zulip/zulip.github.io
## How to get involved with contributing to Zulip
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,11 +172,10 @@ 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
=======
## License
Copyright 2011-2015 Dropbox, Inc.

View File

@@ -38,7 +38,8 @@ These instructions should be followed as root.
(1) Install the SSL certificates for your machine to
`/etc/ssl/private/zulip.key` and `/etc/ssl/certs/zulip.combined-chain.crt`.
If you don't know how to generate an SSL certificate, you, you can
If you don't know how to generate an SSL certificate, you can
do the following to generate a self-signed certificate:
```
@@ -53,17 +54,45 @@ These instructions should be followed as root.
cp zulip.combined-chain.crt /etc/ssl/certs/zulip.combined-chain.crt
```
You will eventually want to get a properly signed certificate (and
note that at present the Zulip desktop app doesn't support
You will eventually want to get a properly signed SSL 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. 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:
You can get a free, properly signed certificate from the [Let's
Encrypt service](https://letsencrypt.org/); here are the simplified
instructions for using it with Zulip (run it all as root):
```
sudo apt-get install git bc nginx
git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
cd /opt/letsencrypt
letsencrypt-auto certonly --standalone
# Now symlink the certificates to make them available where Zulip expects them.
ln -s /etc/letsencrypt/live/your_domain/privkey.pem /etc/ssl/private/zulip.key
ln -s /etc/letsencrypt/live/your_domain/fullchain.pem /etc/ssl/certs/zulip.combined-chain.crt
```
If you already had a webserver installed on the system (e.g. you
previously installed Zulip and are now getting a cert), you will
need to stop the webserver (e.g. `service nginx stop`) and start it
again after (e.g. `service nginx start`) running the above.
Finally, if you want to proceed with just an IP address, it is
possible to finish a Zulip installation that way; just set
EXTERNAL_HOST to be the IP address.
(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 +167,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 +345,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
@@ -377,7 +411,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
@@ -443,7 +477,7 @@ computed using a hash of avatar_salt and user's email), etc.
they do get large on a busy server, and it's definitely
lower-priority.
### Restoration
#### Restoration
To restore from backups, the process is basically the reverse of the above:
@@ -475,10 +509,11 @@ that they are up to date using the Nagios plugin at:
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 doing with Postgres streaming
replication ; you can see the configuration in these files:
#### 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
@@ -488,65 +523,64 @@ 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!
### Using a remote postgres host
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).
### Monitoring Zulip
The complete Nagios configuration (sans secret keys) we used to
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); there are a number of useful Nagios plugins available
there, including:
tarballs).
Frontend server monitoring:
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_website_response.sh (standard HTTP check)
Queue worker monitoring:
* 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_pg_replication_lag
* 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_debian_packages
* check_website_response.sh (standard HTTP check)
Contributions on making it easier to monitor Zulip and maintain it in
production, e.g. https://github.com/zulip/zulip/issues/371, are very
welcome!
* 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 (see
REMOTE_POSTGRES_HOST docs above) for isolation, though it is not
required. In the following, we discuss a relatively simple
* 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.
@@ -593,12 +627,6 @@ running Zulip with a large team (>1000 users).
likely the first part of any project to support exchanging events
amongst multiple servers.
* The first scalability issue encountered by a very large realm (more
than a few thousand users), will be with the [frontend buddy list
perf and UI](https://github.com/zulip/zulip/issues/262). Fixing
this should be a small project; the code for that part of the UI
layer doesn't do proper incremental updates.
Questions, concerns, and bug reports about this area of Zulip are very
welcome! This is an area we are hoping to improve.
@@ -683,15 +711,23 @@ we can do a responsible security announcement).
#### Users and Bots
* There are three types of users in a Zulip realm: Administrators,
normal users, and botsq. Administrators have the ability to
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
messages sent to 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.
can toggle whether that stream is public or private. Also, Zulip
realm administrators have administrative access to the API keys of
all bots in the realm, so a Zulip administrator may be able to
access messages sent to private streams that have bots subscribed,
by using the bot's credentials.
In the future, Zulip's security model may change to allow realm
administrators to access private messages (e.g. to support auditing
functionality).
* 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
@@ -857,10 +893,25 @@ hostname/DNS side of the configuration. Suggestions for how to
improve this SSO setup documentation are very welcome!
Remote Postgresql database
==========================
Postgres database details
=========================
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 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:
@@ -877,10 +928,101 @@ Then you should specify the password of the user zulip for the database in /etc/
postgres_password = xxxx
```
Finally you can stop your database in the zulip server to save some memory, you can do it with:
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
@@ -122,7 +122,7 @@ Copyright: 2013 Nijiko Yonskai
License: Apache-2.0
Comment: The software has been modified.
Files: static/third/gemoji/images/emoji/unicode/* tools/emoji_dump/*.ttf
Files: static/third/gemoji/images/emoji/unicode/* tools/setup/emoji_dump/*.ttf
Copyright: Google, Inc.
License: Apache-2.0
Comment: These are actually Noto Emoji, not gemoji.
@@ -142,7 +142,7 @@ Copyright: 2013 Jack Moore
License: Expat
Files: static/third/jquery-caret/*
Copyright: 2010 C.F., Wong
Copyright: 2012, 2013 Andrew C. Dvorak
License: Expat
Files: static/third/jquery-filedrop/jquery.filedrop.js
@@ -204,7 +204,7 @@ Copyright: 2011-2013 Felix Gnass
License: Expat
Files: static/third/underscore/underscore.js
Copyright: 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
Copyright: 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
License: Expat
Comment: https://github.com/jashkenas/underscore/blob/master/LICENSE
@@ -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

55
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,17 +18,59 @@ 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.
vb.memory = 2048
# It's possible we can get away with just 1GB; more testing needed
vb.memory = 1280
end
$provision_script = <<SCRIPT
set -x
set -e
sudo apt-get update
sudo apt-get install -y python-pbs
ln -nsf /srv/zulip ~/zulip
/usr/bin/python /srv/zulip/provision.py
SCRIPT

View File

@@ -2,6 +2,7 @@ from __future__ import absolute_import
from __future__ import print_function
from django.core.management.base import BaseCommand
from typing import Any
from zerver.models import UserPresence, UserActivity
from zerver.lib.utils import statsd, statsd_key
@@ -15,6 +16,7 @@ class Command(BaseCommand):
Run as a cron job that runs every 10 minutes."""
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
# Get list of all active users in the last 1 week
cutoff = datetime.now() - timedelta(minutes=30, hours=168)
@@ -22,7 +24,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:
@@ -31,7 +33,7 @@ class Command(BaseCommand):
known_active = last_presence.timestamp
for bucket in hour_buckets:
if not bucket in user_info[last_presence.user_profile.realm.domain]:
if bucket not in user_info[last_presence.user_profile.realm.domain]:
user_info[last_presence.user_profile.realm.domain][bucket] = []
if datetime.now(known_active.tzinfo) - known_active < timedelta(hours=bucket):
user_info[last_presence.user_profile.realm.domain][bucket].append(last_presence.user_profile.email)
@@ -40,14 +42,14 @@ class Command(BaseCommand):
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))
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:
if not bucket in user_info[activity.user_profile.realm.domain]:
if bucket not in user_info[activity.user_profile.realm.domain]:
user_info[activity.user_profile.realm.domain][bucket] = []
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)
@@ -55,4 +57,4 @@ class Command(BaseCommand):
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))
statsd.gauge("users.reading.%s.%shr" % (statsd_key(realm, True), statsd_key(hr, True)), len(users))

View File

@@ -5,6 +5,7 @@ import datetime
import pytz
from optparse import make_option
from typing import Any
from django.core.management.base import BaseCommand
from zerver.lib.statistics import activity_averages_during_day
@@ -16,6 +17,7 @@ class Command(BaseCommand):
help="Day to query in format 2013-12-05. Default is yesterday"),)
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if options["date"] is None:
date = datetime.datetime.now() - datetime.timedelta(days=1)
else:

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from optparse import make_option
from django.core.management.base import BaseCommand
from zerver.models import Recipient, Message
@@ -10,6 +12,7 @@ import time
import logging
def compute_stats(log_level):
# type: (int) -> None
logger = logging.getLogger()
logger.setLevel(log_level)
@@ -27,15 +30,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 +47,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. /
@@ -76,6 +79,7 @@ class Command(BaseCommand):
help = "Compute statistics on MIT Zephyr usage."
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
level = logging.INFO
if options["verbose"]:
level = logging.DEBUG

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from typing import Any, Dict
from zerver.lib.statistics import seconds_usage_between
from optparse import make_option
@@ -10,6 +13,7 @@ import datetime
from django.utils.timezone import utc
def analyze_activity(options):
# type: (Dict[str, Any]) -> None
day_start = datetime.datetime.strptime(options["date"], "%Y-%m-%d").replace(tzinfo=utc)
day_end = day_start + datetime.timedelta(days=options["duration"])
@@ -26,7 +30,7 @@ def analyze_activity(options):
continue
total_duration += duration
print("%-*s%s" % (37, user_profile.email, duration, ))
print("%-*s%s" % (37, user_profile.email, duration,))
print("\nTotal Duration: %s" % (total_duration,))
print("\nTotal Duration in minutes: %s" % (total_duration.total_seconds() / 60.,))
@@ -43,7 +47,7 @@ It will correctly not count server-initiated reloads in the activity statistics.
The duration flag can be used to control how many days to show usage duration for
Usage: python2.7 manage.py analyze_user_activity [--realm=zulip.com] [--date=2013-09-10] [--duration=1]
Usage: python manage.py analyze_user_activity [--realm=zulip.com] [--date=2013-09-10] [--duration=1]
By default, if no date is selected 2013-09-10 is used. If no realm is provided, information
is shown for all realms"""
@@ -55,4 +59,5 @@ is shown for all realms"""
)
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
analyze_activity(options)

View File

@@ -1,8 +1,11 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from django.db.models import Count
from django.db.models import Count, QuerySet
from zerver.models import UserActivity, UserProfile, Realm, \
get_realm, get_user_profile_by_email
@@ -14,15 +17,17 @@ class Command(BaseCommand):
Usage examples:
python2.7 manage.py client_activity
python2.7 manage.py client_activity zulip.com
python2.7 manage.py client_activity jesstess@zulip.com"""
python manage.py client_activity
python manage.py client_activity zulip.com
python manage.py client_activity jesstess@zulip.com"""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('arg', metavar='<arg>', type=str, nargs='?', default=None,
help="realm or user to estimate client activity for")
def compute_activity(self, user_activity_objects):
# type: (QuerySet) -> None
# Report data from the past week.
#
# This is a rough report of client activity because we inconsistently
@@ -54,6 +59,7 @@ python2.7 manage.py client_activity jesstess@zulip.com"""
def handle(self, *args, **options):
# type: (*Any, **str) -> None
if options['arg'] is None:
# Report global activity.
self.compute_activity(UserActivity.objects.all())

View File

@@ -1,6 +1,10 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
import datetime
import pytz
@@ -18,50 +22,60 @@ class Command(BaseCommand):
help = "Generate statistics on realm activity."
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('realms', metavar='<realm>', type=str, nargs='*',
help="realm to generate statistics for")
def active_users(self, realm):
# type: (Realm) -> List[UserProfile]
# Has been active (on the website, for now) in the last 7 days.
activity_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=7)
return [activity.user_profile for activity in \
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):
# type: (UserProfile, int) -> int
sent_time_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=days_ago)
return human_messages.filter(sender=user, pub_date__gt=sent_time_cutoff).count()
def total_messages(self, realm, days_ago):
# type: (Realm, int) -> int
sent_time_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=days_ago)
return Message.objects.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).count()
def human_messages(self, realm, days_ago):
# type: (Realm, int) -> int
sent_time_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=days_ago)
return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).count()
def api_messages(self, realm, days_ago):
# type: (Realm, int) -> int
return (self.total_messages(realm, days_ago) - self.human_messages(realm, days_ago))
def stream_messages(self, realm, days_ago):
# type: (Realm, int) -> int
sent_time_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=days_ago)
return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff,
recipient__type=Recipient.STREAM).count()
def private_messages(self, realm, days_ago):
# type: (Realm, int) -> int
sent_time_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=days_ago)
return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).exclude(
recipient__type=Recipient.STREAM).exclude(recipient__type=Recipient.HUDDLE).count()
def group_private_messages(self, realm, days_ago):
# type: (Realm, int) -> int
sent_time_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=days_ago)
return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).exclude(
recipient__type=Recipient.STREAM).exclude(recipient__type=Recipient.PERSONAL).count()
def report_percentage(self, numerator, denominator, text):
# type: (float, float, str) -> None
if not denominator:
fraction = 0.0
else:
@@ -69,6 +83,7 @@ class Command(BaseCommand):
print("%.2f%% of" % (fraction * 100,), text)
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if options['realms']:
try:
realms = [get_realm(domain) for domain in options['realms']]

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from django.db.models import Q
from zerver.models import Realm, Stream, Message, Subscription, Recipient, get_realm
@@ -9,10 +12,12 @@ class Command(BaseCommand):
help = "Generate statistics on the streams for a realm."
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('realms', metavar='<realm>', type=str, nargs='*',
help="realm to generate statistics for")
def handle(self, *args, **options):
# type: (*Any, **str) -> None
if options['realms']:
try:
realms = [get_realm(domain) for domain in options['realms']]

View File

@@ -1,8 +1,10 @@
from __future__ import absolute_import
from __future__ import print_function
from argparse import ArgumentParser
import datetime
import pytz
from typing import Any
from django.core.management.base import BaseCommand
from zerver.models import UserProfile, Realm, Stream, Message, get_realm
@@ -12,15 +14,18 @@ class Command(BaseCommand):
help = "Generate statistics on user activity."
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('realms', metavar='<realm>', type=str, nargs='*',
help="realm to generate statistics for")
def messages_sent_by(self, user, week):
# type: (UserProfile, int) -> int
start = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=(week + 1)*7)
end = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=week*7)
return Message.objects.filter(sender=user, pub_date__gt=start, pub_date__lte=end).count()
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if options['realms']:
try:
realms = [get_realm(domain) for domain in options['realms']]

View File

@@ -1,8 +1,9 @@
from django.conf.urls import patterns, url
urlpatterns = patterns('analytics.views',
url(r'^activity$', 'get_activity'),
url(r'^realm_activity/(?P<realm>[\S]+)/$', 'get_realm_activity'),
url(r'^user_activity/(?P<email>[\S]+)/$', 'get_user_activity'),
)
i18n_urlpatterns = [
url(r'^activity$', 'analytics.views.get_activity'),
url(r'^realm_activity/(?P<realm>[\S]+)/$', 'analytics.views.get_realm_activity'),
url(r'^user_activity/(?P<email>[\S]+)/$', 'analytics.views.get_user_activity'),
]
urlpatterns = patterns('', *i18n_urlpatterns)

View File

@@ -1,10 +1,14 @@
from __future__ import absolute_import
from __future__ import division
from six import text_type
from typing import Any, Dict, List, Tuple, Optional, Sequence, Callable, Union
from django.db import connection
from django.db.models.query import QuerySet
from django.template import RequestContext, loader
from django.utils.html import mark_safe
from django.shortcuts import render_to_response
from django.core import urlresolvers
from django.http import HttpResponseNotFound
from django.http import HttpResponseNotFound, HttpRequest, HttpResponse
from jinja2 import Markup as mark_safe
from zerver.decorator import has_request_variables, REQ, zulip_internal
from zerver.models import get_realm, UserActivity, UserActivityInterval, Realm
@@ -22,10 +26,14 @@ from six.moves import range
from six.moves import zip
eastern_tz = pytz.timezone('US/Eastern')
from zproject.jinja2 import render_to_response
def make_table(title, cols, rows, has_row_class=False):
# type: (str, List[str], List[Any], bool) -> str
if not has_row_class:
def fix_row(row):
# type: (Any) -> Dict[str, Any]
return dict(cells=row, row_class=None)
rows = list(map(fix_row, rows))
@@ -39,6 +47,7 @@ def make_table(title, cols, rows, has_row_class=False):
return content
def dictfetchall(cursor):
# type: (connection.cursor) -> List[Dict[str, Any]]
"Returns all rows from a cursor as a dict"
desc = cursor.description
return [
@@ -48,6 +57,7 @@ def dictfetchall(cursor):
def get_realm_day_counts():
# type: () -> Dict[str, Dict[str, str]]
query = '''
select
r.domain,
@@ -75,18 +85,19 @@ 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']
result = {}
for domain in counts:
cnts = [counts[domain].get(age, 0) for age in range(8)]
min_cnt = min(cnts)
max_cnt = max(cnts)
raw_cnts = [counts[domain].get(age, 0) for age in range(8)]
min_cnt = min(raw_cnts)
max_cnt = max(raw_cnts)
def format_count(cnt):
# type: (int) -> str
if cnt == min_cnt:
good_bad = 'bad'
elif cnt == max_cnt:
@@ -96,12 +107,13 @@ def get_realm_day_counts():
return '<td class="number %s">%s</td>' % (good_bad, cnt)
cnts = ''.join(map(format_count, cnts))
cnts = ''.join(map(format_count, raw_cnts))
result[domain] = dict(cnts=cnts)
return result
def realm_summary_table(realm_minutes):
# type: (Dict[str, float]) -> str
query = '''
SELECT
realm.domain,
@@ -137,7 +149,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 +179,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 +201,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
@@ -211,10 +226,10 @@ def realm_summary_table(realm_minutes):
row['history'] = ''
# augment data with realm_minutes
total_hours = 0
total_hours = 0.0
for row in rows:
domain = row['domain']
minutes = realm_minutes.get(domain, 0)
minutes = realm_minutes.get(domain, 0.0)
hours = minutes / 60.0
total_hours += hours
row['hours'] = str(int(hours))
@@ -229,6 +244,7 @@ def realm_summary_table(realm_minutes):
# Count active sites
def meets_goal(row):
# type: (Dict[str, int]) -> bool
return row['active_user_count'] >= 5
num_active_sites = len(list(filter(meets_goal, rows)))
@@ -237,10 +253,12 @@ def realm_summary_table(realm_minutes):
total_active_user_count = 0
total_user_profile_count = 0
total_bot_count = 0
total_at_risk_count = 0
for row in rows:
total_active_user_count += int(row['active_user_count'])
total_user_profile_count += int(row['user_profile_count'])
total_bot_count += int(row['bot_count'])
total_at_risk_count += int(row['at_risk_count'])
rows.append(dict(
@@ -248,7 +266,8 @@ def realm_summary_table(realm_minutes):
active_user_count=total_active_user_count,
user_profile_count=total_user_profile_count,
bot_count=total_bot_count,
hours=int(total_hours)
hours=int(total_hours),
at_risk_count=total_at_risk_count,
))
content = loader.render_to_string(
@@ -259,6 +278,7 @@ def realm_summary_table(realm_minutes):
def user_activity_intervals():
# type: () -> Tuple[mark_safe, Dict[str, float]]
day_end = timestamp_to_datetime(time.time())
day_start = day_end - timedelta(hours=24)
@@ -298,7 +318,7 @@ def user_activity_intervals():
total_duration += duration
realm_duration += duration
output += " %-*s%s\n" % (37, email, duration, )
output += " %-*s%s\n" % (37, email, duration)
realm_minutes[domain] = realm_duration.total_seconds() / 60
@@ -309,6 +329,7 @@ def user_activity_intervals():
return content, realm_minutes
def sent_messages_report(realm):
# type: (str) -> str
title = 'Recently sent messages for ' + realm
cols = [
@@ -376,7 +397,9 @@ def sent_messages_report(realm):
return make_table(title, cols, rows)
def ad_hoc_queries():
# type: () -> List[Dict[str, str]]
def get_page(query, cols, title):
# type: (str, List[str], str) -> Dict[str, str]
cursor = connection.cursor()
cursor.execute(query)
rows = cursor.fetchall()
@@ -384,6 +407,7 @@ def ad_hoc_queries():
cursor.close()
def fix_rows(i, fixup_func):
# type: (int, Union[Callable[[Realm], mark_safe], Callable[[datetime], str]]) -> None
for row in rows:
row[i] = fixup_func(row[i])
@@ -546,8 +570,9 @@ def ad_hoc_queries():
@zulip_internal
@has_request_variables
def get_activity(request):
duration_content, realm_minutes = user_activity_intervals()
counts_content = realm_summary_table(realm_minutes)
# type: (HttpRequest) -> HttpResponse
duration_content, realm_minutes = user_activity_intervals() # type: Tuple[mark_safe, Dict[str, float]]
counts_content = realm_summary_table(realm_minutes) # type: str
data = [
('Counts', counts_content),
('Durations', duration_content),
@@ -560,10 +585,11 @@ def get_activity(request):
return render_to_response(
'analytics/activity.html',
dict(data=data, title=title, is_home=True),
context_instance=RequestContext(request)
request=request
)
def get_user_activity_records_for_realm(realm, is_bot):
# type: (str, bool) -> QuerySet
fields = [
'user_profile__full_name',
'user_profile__email',
@@ -583,6 +609,7 @@ def get_user_activity_records_for_realm(realm, is_bot):
return records
def get_user_activity_records_for_email(email):
# type: (str) -> List[QuerySet]
fields = [
'user_profile__full_name',
'query',
@@ -599,6 +626,7 @@ def get_user_activity_records_for_email(email):
return records
def raw_user_activity_table(records):
# type: (List[QuerySet]) -> str
cols = [
'query',
'client',
@@ -607,6 +635,7 @@ def raw_user_activity_table(records):
]
def row(record):
# type: (QuerySet) -> List[Any]
return [
record.query,
record.client.name,
@@ -619,8 +648,15 @@ def raw_user_activity_table(records):
return make_table(title, cols, rows)
def get_user_activity_summary(records):
summary = {}
# type: (List[QuerySet]) -> Dict[str, Dict[str, Any]]
#: `Any` used above should be `Union(int, datetime)`.
#: However current version of `Union` does not work inside other function.
#: We could use something like:
# `Union[Dict[str, Dict[str, int]], Dict[str, Dict[str, datetime]]]`
#: but that would require this long `Union` to carry on throughout inner functions.
summary = {} # type: Dict[str, Dict[str, Any]]
def update(action, record):
# type: (str, QuerySet) -> None
if action not in summary:
summary[action] = dict(
count=record.count,
@@ -654,7 +690,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)
@@ -662,24 +698,28 @@ def get_user_activity_summary(records):
return summary
def format_date_for_activity_reports(date):
# type: (Optional[datetime]) -> str
if date:
return date.astimezone(eastern_tz).strftime('%Y-%m-%d %H:%M')
else:
return ''
def user_activity_link(email):
# type: (str) -> mark_safe
url_name = 'analytics.views.get_user_activity'
url = urlresolvers.reverse(url_name, kwargs=dict(email=email))
email_link = '<a href="%s">%s</a>' % (url, email)
return mark_safe(email_link)
def realm_activity_link(realm):
# type: (str) -> mark_safe
url_name = 'analytics.views.get_realm_activity'
url = urlresolvers.reverse(url_name, kwargs=dict(realm=realm))
realm_link = '<a href="%s">%s</a>' % (url, realm)
return mark_safe(realm_link)
def realm_client_table(user_summaries):
# type: (Dict[str, Dict[str, Dict[str, Any]]]) -> str
exclude_keys = [
'internal',
'name',
@@ -724,6 +764,7 @@ def realm_client_table(user_summaries):
return make_table(title, cols, rows)
def user_activity_summary_table(user_summary):
# type: (Dict[str, Dict[str, Any]]) -> str
rows = []
for k, v in user_summary.items():
if k == 'name':
@@ -750,28 +791,33 @@ def user_activity_summary_table(user_summary):
return make_table(title, cols, rows)
def realm_user_summary_table(all_records, admin_emails):
# type: (List[QuerySet], Set[text_type]) -> Tuple[Dict[str, Dict[str, Any]], str]
user_records = {}
def by_email(record):
# type: (QuerySet) -> str
return record.user_profile.email
for email, records in itertools.groupby(all_records, by_email):
user_records[email] = get_user_activity_summary(list(records))
def get_last_visit(user_summary, k):
# type: (Dict[str, Dict[str, datetime]], str) -> Optional[datetime]
if k in user_summary:
return user_summary[k]['last_visit']
else:
return None
def get_count(user_summary, k):
# type: (Dict[str, Dict[str, str]], str) -> str
if k in user_summary:
return user_summary[k]['count']
else:
return ''
def is_recent(val):
age = datetime.now(val.tzinfo) - val
# type: (Optional[datetime]) -> bool
age = datetime.now(val.tzinfo) - val # type: ignore # datetie.now tzinfo bug.
return age.total_seconds() < 5 * 60
rows = []
@@ -781,18 +827,19 @@ def realm_user_summary_table(all_records, admin_emails):
cells = [user_summary['name'], email_link, sent_count]
row_class = ''
for field in ['use', 'send', 'pointer', 'desktop', 'ZulipiOS', 'Android']:
val = get_last_visit(user_summary, field)
visit = get_last_visit(user_summary, field)
if field == 'use':
if val and is_recent(val):
if visit and is_recent(visit):
row_class += ' recently_active'
if email in admin_emails:
row_class += ' admin'
val = format_date_for_activity_reports(val)
val = format_date_for_activity_reports(visit)
cells.append(val)
row = dict(cells=cells, row_class=row_class)
rows.append(row)
def by_used_time(row):
# type: (Dict[str, Sequence[str]]) -> str
return row['cells'][3]
rows = sorted(rows, key=by_used_time, reverse=True)
@@ -816,9 +863,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: (HttpRequest, str) -> HttpResponse
data = [] # type: List[Tuple[str, str]]
all_user_records = {} # type: Dict[str, Any]
try:
admins = get_realm(realm).get_admin_users()
@@ -828,8 +875,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)
@@ -854,14 +900,15 @@ def get_realm_activity(request, realm):
return render_to_response(
'analytics/activity.html',
dict(data=data, realm_link=realm_link, title=title),
context_instance=RequestContext(request)
request=request
)
@zulip_internal
def get_user_activity(request, email):
# type: (HttpRequest, str) -> HttpResponse
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)
@@ -874,5 +921,5 @@ def get_user_activity(request, email):
return render_to_response(
'analytics/activity.html',
dict(data=data, title=title),
context_instance=RequestContext(request)
request=request
)

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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# zulip-send -- Sends a message to the specified recipients.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2012-2014 Zulip, Inc.
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2012 Zulip, Inc.
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2012 Zulip, Inc.
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2014 Zulip, Inc.
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2012 Zulip, Inc.
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2012 Zulip, Inc.
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2012 Zulip, Inc.
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2012 Zulip, Inc.
@@ -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 python
# -*- 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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2012 Zulip, Inc.
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2012 Zulip, Inc.
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright © 2012 Zulip, Inc.
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2014 Zulip, Inc.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Asana integration for Zulip
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2014 Zulip, Inc.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Zulip mirror of Basecamp activity
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2014 Zulip, Inc.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Zulip mirror of Codebase HQ activity
@@ -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__))
@@ -132,7 +135,7 @@ def handle_event(event):
content = "%s deleted branch %s from %s" % (actor_name, branch, project)
else:
if new_ref:
branch = "new branch %s" % (branch, )
branch = "new branch %s" % (branch,)
content = "%s pushed %s commit(s) to %s in project %s:\n\n" % \
(actor_name, num_commits, branch, project)
for commit in raw_props.get('commits'):
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Zulip notification post-receive hook.
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2014 Zulip, Inc.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Zulip hook for Mercurial changeset pushes.

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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
import optparse
import zulip

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
#
# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git.
#
@@ -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
@@ -32,7 +33,7 @@ import stat
try:
from subprocess import CalledProcessError
except ImportError:
# from python2.7:subprocess.py
# from python:subprocess.py
# Exception classes used by this module.
class CalledProcessError(Exception):
"""This exception is raised when a process run by check_call() returns
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2012-2014 Zulip, Inc.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2014 Zulip, Inc.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# RSS integration for Zulip
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Zulip notification post-commit hook.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2014 Zulip, Inc.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Twitter integration for Zulip
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Twitter search integration for Zulip
@@ -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

@@ -1,7 +1,9 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# -*- 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,31 +22,32 @@
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
from typing import Any, Dict
__version__ = "0.2.4"
__version__ = "0.2.5"
logger = logging.getLogger(__name__)
# Check that we have a recent enough version
# Older versions don't provide the 'json' attribute on responses.
assert(LooseVersion(requests.__version__) >= LooseVersion('0.12.1'))
assert(LooseVersion(requests.__version__) >= LooseVersion('0.12.1')) # type: ignore # https://github.com/python/mypy/issues/1165 and https://github.com/python/typeshed/pull/206
# In newer versions, the 'json' attribute is a function, not a property
requests_json_is_function = callable(requests.Response.json)
@@ -164,7 +165,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 +246,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:
@@ -255,7 +256,7 @@ class Client(object):
'had_error_retry': False,
'request': request,
'failures': 0,
}
} # type: Dict[str, Any]
def error_retry(error_string):
if not self.retry_on_errors or query_state["failures"] >= 10:
@@ -289,7 +290,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,
@@ -343,10 +344,15 @@ class Client(object):
"status_code": res.status_code}
@classmethod
def _register(cls, name, url=None, make_request=(lambda request={}: request),
def _register(cls, name, url=None, make_request=None,
method="POST", computed_url=None, **query_kwargs):
if url is None:
url = name
if make_request is None:
def make_request(request=None):
if request is None:
request = {}
return request
def call(self, *args, **kwargs):
request = make_request(*args, **kwargs)
if computed_url is not None:
@@ -357,7 +363,9 @@ class Client(object):
call.__name__ = name
setattr(cls, name, call)
def call_on_each_event(self, callback, event_types=None, narrow=[]):
def call_on_each_event(self, callback, event_types=None, narrow=None):
if narrow is None:
narrow = []
def do_register():
while True:
if event_types is None:
@@ -425,9 +433,11 @@ def _mk_rm_subs(streams):
def _mk_deregister(queue_id):
return {'queue_id': queue_id}
def _mk_events(event_types=None, narrow=[]):
def _mk_events(event_types=None, narrow=None):
if event_types is None:
return dict()
if narrow is None:
narrow = []
return dict(event_types=event_types, narrow=narrow)
def _kwargs_to_dict(**kwargs):
@@ -468,7 +478,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
#!/usr/bin/env python
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,4 +1,6 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
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,9 +1,10 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
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

@@ -1,4 +1,4 @@
#! /usr/bin/env python2.7
#!/usr/bin/env python
#
# EXPERIMENTAL
# IRC <=> Zulip mirroring bot
@@ -93,11 +93,11 @@ class IRCBot(irc.bot.SingleServerIRCBot):
return
self.dcc_connect(address, port)
usage = """python2.7 irc-mirror.py --server=IRC_SERVER --channel=<CHANNEL> --nick-prefix=<NICK> [optional args]
usage = """python irc-mirror.py --server=IRC_SERVER --channel=<CHANNEL> --nick-prefix=<NICK> [optional args]
Example:
python2.7 irc-mirror.py --irc-server=127.0.0.1 --channel='#test' --nick-prefix=username
python irc-mirror.py --irc-server=127.0.0.1 --channel='#test' --nick-prefix=username
--site=https://zulip.example.com --user=irc-bot@example.com
--api-key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# Copyright (C) 2014 Zulip, Inc.
#
# Permission is hereby granted, free of charge, to any person

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
#
# Copyright (C) 2013 Permabit, Inc.
# Copyright (C) 2013--2014 Zulip, Inc.
@@ -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
#!/usr/bin/env python
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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
import sys
import subprocess
import base64
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
import sys
import os
import logging
@@ -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

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# Copyright (C) 2012 Zulip, Inc.
#
# Permission is hereby granted, free of charge, to any person

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# Copyright (C) 2012 Zulip, Inc.
#
# Permission is hereby granted, free of charge, to any person

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2.7
#!/usr/bin/env python
# Copyright (C) 2012 Zulip, Inc.
#
# Permission is hereby granted, free of charge, to any person
@@ -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

@@ -1,53 +0,0 @@
# Change Log
All notable changes to this project will be documented in this file.
[Unreleased]
[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.
- Added partial support for translating the Zulip UI.
- Migrated installing Node dependencies to use npm.
- Fixed LDAP integration breaking autocomplete of @-mentions.
- Fixed admin panel reactivation/deactivation of bots.
- Fixed inaccurate documentation for downloading the desktop apps.
- Fixed various minor bugs in production installation process.
- Fixed security issue where recent history on private streams might
be visible to new users (to the Zulip team) who were invited with that
private stream as one of their initial streams
(https://github.com/zulip/zulip/issues/230).
- Major preliminary progress towards supporting Python 3.
[1.3.7] - 2015-10-19
- Turn off desktop and audible notifications for streams by default.
- Added support for the LDAP authentication integration creating new users.
- Added new endpoint to support Google auth on mobile.
- Fixed desktop notifications in modern Firefox.
- Fixed several installation issues for both production and development environments.
- Improved documentation for outgoing SMTP and the email mirror integration.

View File

@@ -2,23 +2,23 @@
# Copyright: (c) 2008, Jarek Zgoda <jarek.zgoda@gmail.com>
# 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, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing 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 MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR 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
# 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, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing 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 MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR 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.
VERSION = (0, 9, 'pre')

View File

@@ -4,6 +4,7 @@
__revision__ = '$Id: cleanupconfirmation.py 5 2008-11-18 09:10:12Z jarek.zgoda $'
from typing import Any
from django.core.management.base import NoArgsCommand
@@ -14,4 +15,5 @@ class Command(NoArgsCommand):
help = 'Delete expired confirmations from database'
def handle_noargs(self, **options):
# type: (**Any) -> None
Confirmation.objects.delete_expired_confirmations()

View File

@@ -110,4 +110,4 @@ class Confirmation(models.Model):
verbose_name_plural = _('confirmation emails')
def __unicode__(self):
return _('confirmation email for %s') % self.content_object
return _('confirmation email for %s') % (self.content_object,)

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

@@ -9,4 +9,4 @@ from django.conf import settings
def get_status_field(app_label, model_name):
model = '%s.%s' % (app_label, model_name)
mapping = getattr(settings, 'STATUS_FIELDS', {})
return mapping.get(model, 'status')
return mapping.get(model, 'status')

View File

@@ -8,11 +8,14 @@ __revision__ = '$Id: views.py 21 2008-12-05 09:21:03Z jarek.zgoda $'
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.conf import settings
from django.http import HttpRequest, HttpResponse
from confirmation.models import Confirmation
from zproject.jinja2 import render_to_response
def confirm(request, confirmation_key):
# type: (HttpRequest, str) -> HttpResponse
confirmation_key = confirmation_key.lower()
obj = Confirmation.objects.confirm(confirmation_key)
confirmed = True
@@ -38,6 +41,5 @@ def confirm(request, confirmation_key):
]
if obj:
# if we have an object, we can use specific template
templates.insert(0, 'confirmation/confirm_%s.html' % obj._meta.model_name)
return render_to_response(templates, ctx,
context_instance=RequestContext(request))
templates.insert(0, 'confirmation/confirm_%s.html' % (obj._meta.model_name,))
return render_to_response(templates, ctx, request=request)

View File

@@ -1,9 +1,16 @@
from django.conf.urls import patterns, url
from django.views.generic import TemplateView, RedirectView
urlpatterns = patterns('',
i18n_urlpatterns = [
# Zephyr/MIT
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')),
]
urlpatterns = patterns('', *i18n_urlpatterns)

View File

@@ -1,23 +1,23 @@
These docs are written in rST, and are included on the zulip.org website
as well as on each development installation. Many of these docs
have been ported from the internal docs of Zulip Inc.,
and may need to be updated for use in the open source project.
These docs are written in [Commonmark
Markdown](http://commonmark.org/) with a small bit of rST. We've
chosen Markdown because it is [easy to
write](http://commonmark.org/help). The docs are served in production
at [zulip.readthedocs.io](https://zulip.readthedocs.io/en/latest/).
To generate HTML docs locally from rST:
If you want to build the documentation locally (e.g. to test your
changes), the dependencies are automatically installed as part of
Zulip development environment provisioning, and you can build the
documentation using:
* `pip install sphinx`
* In this directory, `make html`. Output appears in a `_build/html` subdirectory.
```
cd docs/
make html
```
To create rST from MediaWiki input:
* Use `pandoc -r mediawiki -w rst` on MediaWiki source.
* Use unescape.py to remove any leftover HTML entities (often inside <pre>
tags and the like).
We can use pandoc to translate mediawiki into reStructuredText, but some things need fixing up:
* Add page titles.
* Review pages for formatting (especially inline code chunks) and content.
* Fix wiki links?
* Add pages to the table of contents (`index.rst`).
You can also usually test your changes by pushing a branch to GitHub
and looking at the content on the GitHub web UI, since GitHub renders
Markdown.
When editing dependencies for the Zulip documentation, you should edit
`requirements/docs.txt` (which is used by ReadTheDocs to build the
documentation quickly, without installing all of Zulip's dependencies).

View File

@@ -0,0 +1,234 @@
Zulip architectural overview
============================
Key Codebases
-------------
The core Zulip application is at
[<https://github.com/zulip/zulip>](https://github.com/zulip/zulip) and
is a web application written in Python 2.7 (soon to also support
Python 3) and using the Django framework. That codebase includes
server-side code and the web client, as well as Python API bindings
and most of our integrations with other services and applications (see
[the directory structure
guide](https://zulip.readthedocs.io/en/latest/directory-structure.html)).
We maintain several separate repositories for integrations and other
glue code: a [Hubot adapter](https://github.com/zulip/hubot-zulip);
integrations with
[Phabricator](https://github.com/zulip/phabricator-to-zulip),
[Jenkins](https://github.com/zulip/zulip-jenkins-plugin),
[Puppet](https://github.com/matthewbarr/puppet-zulip),
[Redmine](https://github.com/zulip/zulip-redmine-plugin), and
[Trello](https://github.com/zulip/trello-to-zulip); [node.js API
bindings](https://github.com/zulip/zulip-node); and our [full-text
search PostgreSQL extension](https://github.com/zulip/tsearch_extras) .
Our mobile clients are separate code repositories:
[Android](https://github.com/zulip/zulip-android), [iOS
(stable)](https://github.com/zulip/zulip-ios) , and [our experimental
React Native iOS app](https://github.com/zulip/zulip-mobile). Our
[desktop application](https://github.com/zulip/zulip-desktop) is also a
separate repository.
We use [Transifex](https://www.transifex.com/zulip/zulip/) to do
translations.
In this overview we'll mainly discuss the core Zulip server and web
application.
Usage assumptions and concepts
------------------------------
Zulip is a real-time web-based chat application meant for companies and
similar groups ranging in size from a small team to more than a thousand
users. It features real-time notifications, message persistence and
search, public group conversations (*streams*), invite-only streams,
private one-on-one and group conversations, inline image previews, team
presence/a buddy list, a rich API, Markdown message support, and several
integrations with other services. The maintainer team aims to support
users who connect to Zulip using dedicated iOS, Android, Linux, Windows,
and Mac OS X clients, as well as people using modern web browsers or
dedicated Zulip API clients.
A server can host multiple Zulip *realms* (organizations) at the same
domain, each of which is a private chamber with its own users, streams,
customizations, and so on. This means that one person might be a user of
multiple Zulip realms. The administrators of a realm can choose whether
to allow anyone to register an account and join, or only allow people
who have been invited, or restrict registrations to members of
particular groups (using email domain names or corporate single-sign-on
login for verification). For more on scalability and security
considerations, see [Zulip in
production](https://github.com/zulip/zulip/blob/master/README.prod.md).
The default Zulip home screen is like a chronologically ordered inbox;
it displays messages, starting at the oldest message that the user
hasn't viewed yet. The home screen displays the most recent messages in
all the streams a user has joined (except for the streams they've
muted), as well as private messages from other users, in strict
chronological order. A user can *narrow* to view only the messages in a
single stream, and can further narrow to focus on a *topic* (thread)
within that stream. Each narrow has its own URL.
Zulip's philosophy is to provide sensible defaults but give the user
fine-grained control over their incoming information flow; a user can
mute topics and streams, and can make fine-grained choices to reduce
real-time notifications they find irrelevant.
Components
----------
### Tornado and Django
We use both the [Tornado](http://www.tornadoweb.org) and
[Django](https://www.djangoproject.com/) Python web frameworks.
Django is the main web application server; Tornado runs the
server-to-client real-time push system. The app servers are configured
by the Supervisor configuration (which explains how to start the server
processes; see "Supervisor" below) and the nginx configuration (which
explains which HTTP requests get sent to which app server).
Tornado is an asynchronous server and is meant specifically to hold open
tens of thousands of long-lived (long-polling or websocket) connections
-- that is to say, routes that maintain a persistent connection from
every running client. For this reason, it's responsible for event
(message) delivery, but not much else. We try to avoid any blocking
calls in Tornado because we don't want to delay delivery to thousands of
other connections (as this would make Zulip very much not real-time).
For instance, we avoid doing cache or database queries inside the
Tornado code paths, since those blocking requests carry a very high
performance penalty for a single-threaded, asynchronous server.
The parts that are activated relatively rarely (e.g. when people type or
click on something) are processed by the Django application server. One
exception to this is that Zulip uses websockets through Tornado to
minimize latency on the code path for **sending** messages.
### nginx
nginx is the front-end web server to all Zulip traffic; it serves static
assets and proxies to Django and Tornado. It handles HTTP requests
according to the rules laid down in the many config files found in
`zulip/puppet/zulip/files/nginx/`.
`zulip/puppet/zulip/files/nginx/zulip-include-frontend/app` is the most
important of these files. It explains what happens when requests come in
from outside.
- In production, all requests to URLs beginning with `/static/` are
served from the corresponding files in `/home/zulip/prod-static/`,
and the production build process (`tools/build-release-tarball`)
compiles, minifies, and installs the static assets into the
`prod-static/` tree form. In development, files are served directly
from `/static/` in the git repository.
- Requests to `/json/get_events`, `/api/v1/events`, and `/sockjs` are
sent to the Tornado server. These are requests to the real-time push
system, because the user's web browser sets up a long-lived TCP
connection with Tornado to serve as [a channel for push
notifications](https://en.wikipedia.org/wiki/Push_technology#Long_Polling).
nginx gets the hostname for the Tornado server via
`puppet/zulip/files/nginx/zulip-include-frontend/upstreams`.
- Requests to all other paths are sent to the Django app via the UNIX
socket `unix:/home/zulip/deployments/fastcgi-socket` (defined in
`puppet/zulip/files/nginx/zulip-include-frontend/upstreams`). We use
`zproject/wsgi.py` to implement FastCGI here (see
`django.core.wsgi`).
### Supervisor
We use [supervisord](http://supervisord.org/) to start server processes,
restart them automatically if they crash, and direct logging.
The config file is
`zulip/puppet/zulip/files/supervisor/conf.d/zulip.conf`. This is where
Tornado and Django are set up, as well as a number of background
processes that process event queues. We use event queues for the kinds
of tasks that are best run in the background because they are
expensive (in terms of performance) and don't have to be synchronous
-- e.g., sending emails or updating analytics. Also see [the queuing
guide](https://zulip.readthedocs.io/en/latest/queuing.html).
### memcached
memcached is used to cache database model objects. `zerver/lib/cache.py`
and `zerver/lib/cache_helpers.py` manage putting things into memcached,
and invalidating the cache when values change. The memcached
configuration is in `puppet/zulip/files/memcached.conf`.
### Redis
Redis is used for a few very short-term data stores, such as in the
basis of `zerver/lib/rate_limiter.py`, a per-user rate limiting scheme
[example](http://blog.domaintools.com/2013/04/rate-limiting-with-redis/)),
and the [email-to-Zulip
integration](https://zulip.com/integrations/#email).
Redis is configured in `zulip/puppet/zulip/files/redis` and it's a
pretty standard configuration except for the last line, which turns off
persistence:
# Zulip-specific configuration: disable saving to disk.
save ""
memcached was used first and then we added Redis specifically to
implement rate limiting. [We're discussing switching everything over to
Redis.](https://github.com/zulip/zulip/issues/16)
### RabbitMQ
RabbitMQ is a queueing system. Its config files live in
`zulip/puppet/zulip/files/rabbitmq`. Initial configuration happens in
`zulip/scripts/setup/configure-rabbitmq`.
We use RabbitMQ for queuing expensive work (e.g. sending emails
triggered by a message, push notifications, some analytics, etc.) that
require reliable delivery but which we don't want to do on the main
thread. It's also used for communication between the application server
and the Tornado push system.
Two simple wrappers around `pika` (the Python RabbitMQ client) are in
`zulip/server/lib/queue.py`. There's an asynchronous client for use in
Tornado and a more general client for use elsewhere.
`zerver/lib/event_queue.py` has helper functions for putting events into
one queue or another. Most of the processes started by Supervisor are
queue processors that continually pull things out of a RabbitMQ queue
and handle them.
Also see [the queuing
guide](https://zulip.readthedocs.io/en/latest/queuing.html).
### PostgreSQL
PostgreSQL (also known as Postgres) is the database that stores all
persistent data, that is, data that's expected to live beyond a user's
current session.
In production, Postgres is installed with a default configuration. The
directory that would contain configuration files
(`puppet/zulip/files/postgresql`) has only a utility script and a custom
list of stopwords used by a Postgresql extension.
In a development environment, configuration of that postgresql extension
is handled by `tools/postgres-init-dev-db` (invoked by `provision.py`).
That file also manages setting up the development postgresql user.
`provision.py` also invokes `tools/do-destroy-rebuild-database` to
create the actual database with its schema.
### Nagios
Nagios is an optional component used for notifications to the system
administrator, e.g., in case of outages.
`zulip/puppet/zulip/manifests/nagios.pp` installs Nagios plugins from
puppet/`zulip/files/nagios_plugins/`.
This component is intended to install Nagios plugins intended to be run
on a Nagios server; most of the Zulip Nagios plugins are intended to be
run on the Zulip servers themselves, and are included with the relevant
component of the Zulip server (e.g.
`puppet/zulip/manifests/postgres_common.pp` installs a few under
`/usr/lib/nagios/plugins/zulip_postgres_common`).

125
docs/changelog.md Normal file
View File

@@ -0,0 +1,125 @@
# Version History
All notable changes to the Zulip server are documented in this file.
### Unreleased
### 1.3.13 - 2016-06-21
- Added nearly complete internationalization of the Zulip UI.
- Added warning when using @all/@everyone.
- Added button offering to subscribe at bottom of narrows to streams
the user is not subscribed to.
- Added integrations with Airbrake, CircleCI, Crashlytics, IFTTT,
Transifex, and Updown.io.
- Added menu option to mark all messages in a stream or topic as read.
- Added new Attachment model to keep track of uploaded files.
- Added caching of virtualenvs in development.
- Added mypy static type annotations to about 85% of the Zulip Python codebase.
- Added automated test of backend templates to test for regressions.
- Added lots of detailed documentation on the Zulip development environment.
- Added setting allowing only administrators to create new streams.
- Added button to exit the Zulip tutorial early.
- Added web UI for configuring default streams.
- Added new OPEN_REALM_CREATION setting (default off), providing a UI
for creating additional realms on a Zulip server.
- Fixed email_gateway_password secret not working properly.
- Fixed missing helper scripts for RabbitMQ Nagios plugins.
- Fixed skipping forward to latest messages ("More messages below" button).
- Fixed netcat issue causing Zulip installation to hang on Scaleway machines.
- Fixed rendering of /me status messages after message editing.
- Fixed case sensitivity of right sidebar fading when compose is open.
- Fixed error messages when composing to invalid PM recipients.
- Fixed LDAP auth backend not working with Zulip mobile apps.
- Fixed erroneous WWW-Authenticate headers with expired sessions.
- Changed "coworkers" to "users" in the Zulip UI.
- Changed add_default_stream REST API to correctly use PUT rather than PATCH.
- Updated the Zulip emoji set (the Android Emoji) to a modern version.
- Made numerous small improvements to the Zulip development experience.
- Migrated backend templates to the faster Jinja2 templating system.
- Migrated development environment setup scripts to tools/setup/.
- Expanded test coverage for several areas of the product.
- Simplified the API for writing new webhook integrations.
- Removed most of the remaining javascript global variables.
### 1.3.12 - 2016-05-10
- CVE-2016-4426: Bot API keys were accessible to other users in the same realm.
- CVE-2016-4427: Deactivated users could access messages if SSO was enabled.
- Fixed a RabbitMQ configuration bug that resulted in reordered messages.
- Added expansive test suite for authentication backends and decorators.
- Added an option to logout_all_users to delete only sessions for deactivated users.
### 1.3.11 - 2016-05-02
- 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 - 2016-01-21
- 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.
- Added partial support for translating the Zulip UI.
- Migrated installing Node dependencies to use npm.
- Fixed LDAP integration breaking autocomplete of @-mentions.
- Fixed admin panel reactivation/deactivation of bots.
- Fixed inaccurate documentation for downloading the desktop apps.
- Fixed various minor bugs in production installation process.
- Fixed security issue where recent history on private streams might
be visible to new users (to the Zulip team) who were invited with that
private stream as one of their initial streams
(https://github.com/zulip/zulip/issues/230).
- Major preliminary progress towards supporting Python 3.
### 1.3.7 - 2015-10-19
- Turn off desktop and audible notifications for streams by default.
- Added support for the LDAP authentication integration creating new users.
- Added new endpoint to support Google auth on mobile.
- Fixed desktop notifications in modern Firefox.
- Fixed several installation issues for both production and development environments.
- Improved documentation for outgoing SMTP and the email mirror integration.

View File

@@ -0,0 +1,24 @@
=======================
Code contribution guide
=======================
Thanks for contributing to Zulip! We hope this guide helps make the
process smooth sailing for you.
The "Ways to Contribute" and "How to get involved" sections of
:doc:`readme-symlink` are a good place to start! After that, check out
the tips below.
.. toctree::
:maxdepth: 2
code-style
markdown
testing
logging
mypy
settings
front-end-build-process
queuing
schema-migrations

482
docs/code-style.md Normal file
View File

@@ -0,0 +1,482 @@
Code style and conventions
==========================
Be consistent!
--------------
Look at the surrounding code, or a similar part of the project, and try
to do the same thing. If you think the other code has actively bad
style, fix it (in a separate commit).
When in doubt, send an email to <zulip-devel@googlegroups.com> with your
question.
Lint tools
----------
You can run them all at once with
./tools/lint-all
You can set this up as a local Git commit hook with
``tools/setup-git-repo``
The Vagrant setup process runs this for you.
`lint-all` runs many lint checks in parallel, including
- Javascript ([JSLint](http://www.jslint.com/))
> `tools/jslint/check-all.js` contains a pretty fine-grained set of
> JSLint options, rule exceptions, and allowed global variables. If
> you add a new global, you'll need to add it to the list.
- Python ([Pyflakes](http://pypi.python.org/pypi/pyflakes))
- templates
- Puppet configuration
- custom checks (e.g. trailing whitespace and spaces-not-tabs)
Secrets
-------
Please don't put any passwords, secret access keys, etc. inline in the
code. Instead, use the `get_secret` function in `zproject/settings.py`
to read secrets from `/etc/zulip/secrets.conf`.
Dangerous constructs
--------------------
### Misuse of database queries
Look out for Django code like this:
[Foo.objects.get(id=bar.x.id)
for bar in Bar.objects.filter(...)
if bar.baz < 7]
This will make one database query for each `Bar`, which is slow in
production (but not in local testing!). Instead of a list comprehension,
write a single query using Django's [QuerySet
API](https://docs.djangoproject.com/en/dev/ref/models/querysets/).
If you can't rewrite it as a single query, that's a sign that something
is wrong with the database schema. So don't defer this optimization when
performing schema changes, or else you may later find that it's
impossible.
### UserProfile.objects.get() / Client.objects.get / etc.
In our Django code, never do direct `UserProfile.objects.get(email=foo)`
database queries. Instead always use `get_user_profile_by_{email,id}`.
There are 3 reasons for this:
1. It's guaranteed to correctly do a case-inexact lookup
2. It fetches the user object from remote cache, which is faster
3. 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 remote cache.
### Using Django model objects as keys in sets/dicts
Don't use Django model objects as keys in sets/dictionaries -- you will
get unexpected behavior when dealing with objects obtained from
different database queries:
For example,
`UserProfile.objects.only("id").get(id=17) in set([UserProfile.objects.get(id=17)])`
is False
You should work with the IDs instead.
### user\_profile.save()
You should always pass the update\_fields keyword argument to .save()
when modifying an existing Django model object. By default, .save() will
overwrite every value in the column, which results in lots of race
conditions where unrelated changes made by one thread can be
accidentally overwritten by another thread that fetched its UserProfile
object before the first thread wrote out its change.
### Using raw saves to update important model objects
In most cases, we already have a function in zephyr/lib/actions.py with
a name like do\_activate\_user that will correctly handle lookups,
caching, and notifying running browsers via the event system about your
change. So please check whether such a function exists before writing
new code to modify a model object, since your new code has a good chance
of getting at least one of these things wrong.
### `x.attr('zid')` vs. `rows.id(x)`
Our message row DOM elements have a custom attribute `zid` which
contains the numerical message ID. **Don't access this directly as**
`x.attr('zid')` ! The result will be a string and comparisons (e.g. with
`<=`) will give the wrong result, occasionally, just enough to make a
bug that's impossible to track down.
You should instead use the `id` function from the `rows` module, as in
`rows.id(x)`. This returns a number. Even in cases where you do want a
string, use the `id` function, as it will simplify future code changes.
In most contexts in JavaScript where a string is needed, you can pass a
number without any explicit conversion.
### Javascript var
Always declare Javascript variables using `var`:
var x = ...;
In a function, `var` is necessary or else `x` will be a global variable.
For variables declared at global scope, this has no effect, but we do it
for consistency.
Javascript has function scope only, not block scope. This means that a
`var` declaration inside a `for` or `if` acts the same as a `var`
declaration at the beginning of the surrounding `function`. To avoid
confusion, declare all variables at the top of a function.
### Javascript `for (i in myArray)`
Don't use it:
[[1]](http://stackoverflow.com/questions/500504/javascript-for-in-with-arrays),
[[2]](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml#for-in_loop),
[[3]](http://www.jslint.com/lint.html#forin)
### jQuery global state
Don't mess with jQuery global state once the app has loaded. Code like
this is very dangerous:
$.ajaxSetup({ async: false });
$.get(...);
$.ajaxSetup({ async: true });
jQuery and the browser are free to run other code while the request is
pending, which could perform other Ajax requests with the altered
settings.
Instead, switch to the more general `$.ajax`\_ function, which can take
options like `async`.
### State and logs files
Do not write state and logs files inside the current working directory
in the production environment. This will not how you expect, because the
current working directory for the app changes every time we do a deploy.
Instead, hardcode a path in settings.py -- see SERVER\_LOG\_PATH in
settings.py for an example.
JS array/object manipulation
----------------------------
For generic functions that operate on arrays or JavaScript objects, you
should generally use [Underscore](http://underscorejs.org/). We used to
use jQuery's utility functions, but the Underscore equivalents are more
consistent, better-behaved and offer more choices.
A quick conversion table:
$.each → _.each (parameters to the callback reversed)
$.inArray → _.indexOf (parameters reversed)
$.grep → _.filter
$.map → _.map
$.extend → _.extend
There's a subtle difference in the case of `_.extend`; it will replace
attributes with undefined, whereas jQuery won't:
$.extend({foo: 2}, {foo: undefined}); // yields {foo: 2}, BUT...
_.extend({foo: 2}, {foo: undefined}); // yields {foo: undefined}!
Also, `_.each` does not let you break out of the iteration early by
returning false, the way jQuery's version does. If you're doing this,
you probably want `_.find`, `_.every`, or `_.any`, rather than 'each'.
Some Underscore functions have multiple names. You should always use the
canonical name (given in large print in the Underscore documentation),
with the exception of `_.any`, which we prefer over the less clear
'some'.
More arbitrary style things
---------------------------
### General
Indentation is four space characters for Python, JS, CSS, and shell
scripts. Indentation is two space characters for HTML templates.
We never use tabs anywhere in source code we write, but we have some
third-party files which contain tabs.
Keep third-party static files under the directory
`zephyr/static/third/`, with one subdirectory per third-party project.
We don't have an absolute hard limit on line length, but we should avoid
extremely long lines. A general guideline is: refactor stuff to get it
under 85 characters, unless that makes the code a lot uglier, in which
case it's fine to go up to 120 or so.
Whitespace guidelines:
- Put one space (or more for alignment) around binary arithmetic and
equality operators.
- Put one space around each part of the ternary operator.
- Put one space between keywords like `if` and `while` and their
associated open paren.
- Put one space between the closing paren for `if` and `while`-like
constructs and the opening curly brace. Put the curly brace on the
same line unless doing otherwise improves readability.
- Put no space before or after the open paren for function calls and
no space before the close paren for function calls.
- For the comma operator and colon operator in languages where it is
used for inline dictionaries, put no space before it and at least
one space after. Only use more than one space for alignment.
### Javascript
Don't use `==` and `!=` because these operators perform type coercions,
which can mask bugs. Always use `===` and `!==`.
End every statement with a semicolon.
`if` statements with no braces are allowed, if the body is simple and
its extent is abundantly clear from context and formatting.
Anonymous functions should have spaces before and after the argument
list:
var x = function (foo, bar) { // ...
When calling a function with an anonymous function as an argument, use
this style:
$.get('foo', function (data) {
var x = ...;
// ...
});
The inner function body is indented one level from the outer function
call. The closing brace for the inner function and the closing
parenthesis for the outer call are together on the same line. This style
isn't necessarily appropriate for calls with multiple anonymous
functions or other arguments following them.
Use
$(function () { ...
rather than
$(document).ready(function () { ...
and combine adjacent on-ready functions, if they are logically related.
The best way to build complicated DOM elements is a Mustache template
like `zephyr/static/templates/message.handlebars`. For simpler things
you can use jQuery DOM building APIs like so:
var new_tr = $('<tr />').attr('id', zephyr.id);
Passing a HTML string to jQuery is fine for simple hardcoded things:
foo.append('<p id="selected">foo</p>');
but avoid programmatically building complicated strings.
We used to favor attaching behaviors in templates like so:
<p onclick="select_zephyr({{id}})">
but there are some reasons to prefer attaching events using jQuery code:
- Potential huge performance gains by using delegated events where
possible
- When calling a function from an `onclick` attribute, `this` is not
bound to the element like you might think
- jQuery does event normalization
Either way, avoid complicated JavaScript code inside HTML attributes;
call a helper function instead.
### HTML / CSS
Don't use the `style=` attribute. Instead, define logical classes and
put your styles in `zulip.css`.
Don't use the tag name in a selector unless you have to. In other words,
use `.foo` instead of `span.foo`. We shouldn't have to care if the tag
type changes in the future.
Don't use inline event handlers (`onclick=`, etc. attributes). Instead,
attach a jQuery event handler
(`$('#foo').on('click', function () {...})`) when the DOM is ready
(inside a `$(function () {...})` block).
Use this format when you have the same block applying to multiple CSS
styles (separate lines for each selector):
selector1,
selector2 {
};
### Python
- Scripts should start with `#!/usr/bin/env python` and not
`#/usr/bin/python` (the right Python may not be installed in
`/usr/bin`) or `#/usr/bin/env/python2.7` (bad for Python 3
compatibility). Don't put a shebang line on a Python file unless
it's meaningful to run it as a script. (Some libraries can also be
run as scripts, e.g. to run a test suite.)
- The first import in a file should be
`from __future__ import absolute_import`, per [PEP
328](http://docs.python.org/2/whatsnew/2.5.html#pep-328-absolute-and-relative-imports)
- Put all imports together at the top of the file, absent a compelling
reason to do otherwise.
- Unpacking sequences doesn't require list brackets:
[x, y] = xs # unnecessary
x, y = xs # better
- For string formatting, use `x % (y,)` rather than `x % y`, to avoid
ambiguity if `y` happens to be a tuple.
- When selecting by id, don't use `foo.pk` when you mean `foo.id`.
E.g.
recipient = Recipient(type_id=huddle.pk, type=Recipient.HUDDLE)
should be written as
recipient = Recipient(type_id=huddle.id, type=Recipient.HUDDLE)
in case we ever change the primary keys.
Version Control
---------------
### Commit Discipline
We follow the Git project's own commit discipline practice of "Each
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
makes the 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:
- It should pass tests (so test updates needed by a change should be
in the same commit as the original change, not a separate "fix the
tests that were broken by the last commit" commit).
- It should be safe to deploy individually, or comment in detail in
the commit message as to why it isn't (maybe with a [manual] tag).
So implementing a new API endpoint in one commit and then adding the
security checks in a future commit should be avoided -- the security
checks should be there from the beginning.
- Error handling should generally be included along with the code that
might trigger the error.
- TODO comments should be in the commit that introduces the issue or
functionality with further work required.
When you should be minimal:
- Significant refactorings should be done in a separate commit from
functional changes.
- Moving code from one file to another should be done in a separate
commits from functional changes or even refactoring within a file.
- 2 different refactorings should be done in different commits.
- 2 different features should be done in different commits.
- If you find yourself writing a commit message that reads like a list
of somewhat dissimilar things that you did, you probably should have
just done 2 commits.
When not to be overly minimal:
- For completely new features, you don't necessarily need to split out
new commits for each little subfeature of the new feature. E.g. if
you're writing a new tool from scratch, it's fine to have the
initial tool have plenty of options/features without doing separate
commits for each one. That said, reviewing a 2000-line giant blob of
new code isn't fun, so please be thoughtful about submitting things
in reviewable units.
- Don't bother to split back end commits from front end commits, even
though the backend can often be coherent on its own.
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.
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
- The first line of commit messages should be written in the
imperative and be kept relatively short while concisely explaining
what the commit does. For example:
Bad:
bugfix
gather_subscriptions was broken
fix bug #234.
Good:
Fix gather_subscriptions throwing an exception when given bad input.
- 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.
- 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."
- 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
attention applied to.
### Tests
All significant new features should come with tests. See testing.
### Third party code
When adding new third-party packages to our codebase, please include
"[third]" at the beginning of the commit message. You don't necessarily
need to do this when patching third-party code that's already in tree.

View File

@@ -1,484 +0,0 @@
==========================
Code style and conventions
==========================
Be consistent!
==============
Look at the surrounding code, or a similar part of the project, and
try to do the same thing. If you think the other code has actively bad
style, fix it (in a separate commit).
When in doubt, send an email to zulip-devel@googlegroups.com with your
question.
Lint tools
==========
You can run them all at once with
::
./tools/lint-all
You can set this up as a local Git commit hook with
::
``tools/setup-git-repo``
The Vagrant setup process runs this for you.
``lint-all`` runs many lint checks in parallel, including
- Javascript (`JSLint <http://www.jslint.com/>`__)
``tools/jslint/check-all.js`` contains a pretty fine-grained set of
JSLint options, rule exceptions, and allowed global variables. If you
add a new global, you'll need to add it to the list.
- Python (`Pyflakes <http://pypi.python.org/pypi/pyflakes>`__)
- templates
- Puppet configuration
- custom checks (e.g. trailing whitespace and spaces-not-tabs)
Secrets
=======
Please don't put any passwords, secret access keys, etc. inline in the
code. Instead, use the ``get_secret`` function in
``zproject/settings.py`` to read secrets from ``/etc/zulip/secrets.conf``.
Dangerous constructs
====================
Misuse of database queries
--------------------------
Look out for Django code like this::
[Foo.objects.get(id=bar.x.id)
for bar in Bar.objects.filter(...)
if  bar.baz < 7]
This will make one database query for each ``Bar``, which is slow in
production (but not in local testing!). Instead of a list comprehension,
write a single query using Django's `QuerySet
API <https://docs.djangoproject.com/en/dev/ref/models/querysets/>`__.
If you can't rewrite it as a single query, that's a sign that something
is wrong with the database schema. So don't defer this optimization when
performing schema changes, or else you may later find that it's
impossible.
UserProfile.objects.get() / Client.objects.get / etc.
-----------------------------------------------------
In our Django code, never do direct
``UserProfile.objects.get(email=foo)`` database queries. Instead always
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 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.
Using Django model objects as keys in sets/dicts
------------------------------------------------
Don't use Django model objects as keys in sets/dictionaries -- you will
get unexpected behavior when dealing with objects obtained from
different database queries:
For example,
``UserProfile.objects.only("id").get(id=17) in set([UserProfile.objects.get(id=17)])``
is False
You should work with the IDs instead.
user\_profile.save()
--------------------
You should always pass the update\_fields keyword argument to .save()
when modifying an existing Django model object. By default, .save() will
overwrite every value in the column, which results in lots of race
conditions where unrelated changes made by one thread can be
accidentally overwritten by another thread that fetched its UserProfile
object before the first thread wrote out its change.
Using raw saves to update important model objects
-------------------------------------------------
In most cases, we already have a function in zephyr/lib/actions.py with
a name like do\_activate\_user that will correctly handle lookups,
caching, and notifying running browsers via the event system about your
change. So please check whether such a function exists before writing
new code to modify a model object, since your new code has a good chance
of getting at least one of these things wrong.
``x.attr('zid')`` vs. ``rows.id(x)``
------------------------------------
Our message row DOM elements have a custom attribute ``zid`` which
contains the numerical message ID. **Don't access this directly as**
``x.attr('zid')`` ! The result will be a string and comparisons (e.g.
with ``<=``) will give the wrong result, occasionally, just enough to
make a bug that's impossible to track down.
You should instead use the ``id`` function from the ``rows`` module, as
in ``rows.id(x)``. This returns a number. Even in cases where you do
want a string, use the ``id`` function, as it will simplify future code
changes. In most contexts in JavaScript where a string is needed, you
can pass a number without any explicit conversion.
Javascript var
--------------
Always declare Javascript variables using ``var``::
var x = ...;
In a function, ``var`` is necessary or else ``x`` will be a global
variable. For variables declared at global scope, this has no effect,
but we do it for consistency.
Javascript has function scope only, not block scope. This means that a
``var`` declaration inside a ``for`` or ``if`` acts the same as a
``var`` declaration at the beginning of the surrounding ``function``. To
avoid confusion, declare all variables at the top of a function.
Javascript ``for (i in myArray)``
---------------------------------
Don't use it:
`[1] <http://stackoverflow.com/questions/500504/javascript-for-in-with-arrays>`__,
`[2] <http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml#for-in_loop>`__,
`[3] <http://www.jslint.com/lint.html#forin>`__
jQuery global state
-------------------
Don't mess with jQuery global state once the app has loaded. Code like
this is very dangerous::
$.ajaxSetup({ async: false });
$.get(...);
$.ajaxSetup({ async: true });
jQuery and the browser are free to run other code while the request is
pending, which could perform other Ajax requests with the altered
settings.
Instead, switch to the more general |ajax|_ function, which can take options
like ``async``.
.. |ajax| replace:: ``$.ajax``
.. _ajax: http://api.jquery.com/jQuery.ajax
State and logs files
--------------------
Do not write state and logs files inside the current working directory
in the production environment. This will not how you expect, because the
current working directory for the app changes every time we do a deploy.
Instead, hardcode a path in settings.py -- see SERVER\_LOG\_PATH in
settings.py for an example.
JS array/object manipulation
============================
For generic functions that operate on arrays or JavaScript objects, you
should generally use `Underscore <http://underscorejs.org/>`__. We used
to use jQuery's utility functions, but the Underscore equivalents are
more consistent, better-behaved and offer more choices.
A quick conversion table::
   $.each  _.each (parameters to the callback reversed)
   $.inArray  _.indexOf (parameters reversed)
   $.grep  _.filter
   $.map  _.map
   $.extend  _.extend
There's a subtle difference in the case of ``_.extend``; it will replace
attributes with undefined, whereas jQuery won't::
   $.extend({foo: 2}, {foo: undefined});  // yields {foo: 2}, BUT...
   _.extend({foo: 2}, {foo: undefined});  // yields {foo: undefined}!
Also, ``_.each`` does not let you break out of the iteration early by
returning false, the way jQuery's version does. If you're doing this,
you probably want ``_.find``, ``_.every``, or ``_.any``, rather than
'each'.
Some Underscore functions have multiple names. You should always use the
canonical name (given in large print in the Underscore documentation),
with the exception of ``_.any``, which we prefer over the less clear
'some'.
More arbitrary style things
===========================
General
-------
Indentation is four space characters for Python, JS, CSS, and shell
scripts. Indentation is two space characters for HTML templates.
We never use tabs anywhere in source code we write, but we have some
third-party files which contain tabs.
Keep third-party static files under the directory
``zephyr/static/third/``, with one subdirectory per third-party project.
We don't have an absolute hard limit on line length, but we should avoid
extremely long lines. A general guideline is: refactor stuff to get it
under 85 characters, unless that makes the code a lot uglier, in which
case it's fine to go up to 120 or so.
Whitespace guidelines:
- Put one space (or more for alignment) around binary arithmetic and
equality operators.
- Put one space around each part of the ternary operator.
- Put one space between keywords like ``if`` and ``while`` and their
associated open paren.
- Put one space between the closing paren for ``if`` and ``while``-like
constructs and the opening curly brace. Put the curly brace on the
same line unless doing otherwise improves readability.
- Put no space before or after the open paren for function calls and no
space before the close paren for function calls.
- For the comma operator and colon operator in languages where it is
used for inline dictionaries, put no space before it and at least one
space after. Only use more than one space for alignment.
Javascript
----------
Don't use ``==`` and ``!=`` because these operators perform type
coercions, which can mask bugs. Always use ``===`` and ``!==``.
End every statement with a semicolon.
``if`` statements with no braces are allowed, if the body is simple and
its extent is abundantly clear from context and formatting.
Anonymous functions should have spaces before and after the argument
list::
var x = function (foo, bar) { // ...
When calling a function with an anonymous function as an argument, use
this style::
$.get('foo', function (data) {
    var x = ...;
    // ...
});
The inner function body is indented one level from the outer function
call. The closing brace for the inner function and the closing
parenthesis for the outer call are together on the same line. This style
isn't necessarily appropriate for calls with multiple anonymous
functions or other arguments following them.
Use
::
$(function () { ...
rather than
::
$(document).ready(function () { ...
and combine adjacent on-ready functions, if they are logically related.
The best way to build complicated DOM elements is a Mustache template
like ``zephyr/static/templates/message.handlebars``. For simpler things
you can use jQuery DOM building APIs like so::
var new_tr = $('<tr />').attr('id', zephyr.id);
Passing a HTML string to jQuery is fine for simple hardcoded things::
foo.append('<p id="selected">foo</p>');
but avoid programmatically building complicated strings.
We used to favor attaching behaviors in templates like so::
<p onclick="select_zephyr({{id}})">
but there are some reasons to prefer attaching events using jQuery code:
- Potential huge performance gains by using delegated events where
possible
- When calling a function from an ``onclick`` attribute, ``this`` is
not bound to the element like you might think
- jQuery does event normalization
Either way, avoid complicated JavaScript code inside HTML attributes;
call a helper function instead.
HTML / CSS
----------
Don't use the ``style=`` attribute. Instead, define logical classes and
put your styles in ``zulip.css``.
Don't use the tag name in a selector unless you have to. In other words,
use ``.foo`` instead of ``span.foo``. We shouldn't have to care if the
tag type changes in the future.
Don't use inline event handlers (``onclick=``, etc. attributes).
Instead, attach a jQuery event handler
(``$('#foo').on('click', function () {...})``) when the DOM is ready
(inside a ``$(function () {...})`` block).
Use this format when you have the same block applying to multiple CSS
styles (separate lines for each selector)::
selector1,
selector2 {
};
Python
------
- Scripts should start with ``#!/usr/bin/env python2.7`` and not
``#!/usr/bin/env python2.7``. See commit ``437d4aee`` for an explanation of
why. Don't put such a line on a Python file unless it's meaningful to
run it as a script. (Some libraries can also be run as scripts, e.g.
to run a test suite.)
- The first import in a file should be
``from __future__ import absolute_import``, per `PEP
328 <http://docs.python.org/2/whatsnew/2.5.html#pep-328-absolute-and-relative-imports>`__
- Put all imports together at the top of the file, absent a compelling
reason to do otherwise.
- Unpacking sequences doesn't require list brackets::
[x, y] = xs    # unnecessary
x, y = xs      # better
- For string formatting, use ``x % (y,)`` rather than ``x % y``, to
avoid ambiguity if ``y`` happens to be a tuple.
- When selecting by id, don't use ``foo.pk`` when you mean ``foo.id``.
E.g.
::
recipient = Recipient(type_id=huddle.pk, type=Recipient.HUDDLE)
should be written as
::
recipient = Recipient(type_id=huddle.id, type=Recipient.HUDDLE)
in case we ever change the primary keys.
Version Control
===============
Commit Discipline
-----------------
We follow the Git project's own commit discipline practice of "Each
commit is a minimal coherent idea".
Coherency requirements for any commit:
- It should pass tests (so test updates needed by a change should be in
the same commit as the original change, not a separate "fix the tests
that were broken by the last commit" commit).
- It should be safe to deploy individually, or comment in detail in the
commit message as to why it isn't (maybe with a [manual] tag). So
implementing a new API endpoint in one commit and then adding the
security checks in a future commit should be avoided -- the security
checks should be there from the beginning.
- Error handling should generally be included along with the code that
might trigger the error.
- TODO comments should be in the commit that introduces the
issue or functionality with further work required.
When you should be minimal:
- Significant refactorings should be done in a separate commit from
functional changes.
- Moving code from one file to another should be done in a separate
commits from functional changes or even refactoring within a file.
- 2 different refactorings should be done in different commits.
- 2 different features should be done in different commits.
- If you find yourself writing a commit message that reads like a list
of somewhat dissimilar things that you did, you probably should have
just done 2 commits.
When not to be overly minimal:
- For completely new features, you don't necessarily need to split out
new commits for each little subfeature of the new feature. E.g. if
you're writing a new tool from scratch, it's fine to have the initial
tool have plenty of options/features without doing separate commits
for each one. That said, reviewing a 2000-line giant blob of new
code isn't fun, so please be thoughtful about submitting things in
reviewable units.
- Don't bother to split back end commits from front end commits, even
though the backend can often be coherent on its own.
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.
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.
Commit Messages
---------------
- The first line of commit messages should be written in the imperative
and be kept relatively short while concisely explaining what the
commit does. For example:
Bad::
bugfix
gather_subscriptions was broken
Good::
Prevent gather_subscriptions from throwing an exception when given bad input.
- Please use a complete sentence, 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.
- 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
attention applied to.
Tests
-----
All significant new features should come with tests.
Third party code
----------------
When adding new third-party packages to our codebase, please include
"[third]" at the beginning of the commit message. You don't necessarily
need to do this when patching third-party code that's already in tree.

View File

@@ -15,6 +15,7 @@
import sys
import os
import shlex
from typing import Dict, List, Optional
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -29,16 +30,11 @@ import shlex
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
extensions = [] # type: List[str]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
@@ -64,7 +60,7 @@ release = '0.1'
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = None # type: Optional[str]
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
@@ -227,7 +223,7 @@ latex_elements = {
# Latex figure (float) alignment
#'figure_align': 'htbp',
}
} # type: Dict[str, str]
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
@@ -293,3 +289,13 @@ 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,
}
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
source_suffix = ['.rst', '.md']

115
docs/directory-structure.md Normal file
View File

@@ -0,0 +1,115 @@
Directory structure
===================
This page documents the Zulip directory structure and how to decide
where to put a file.
Scripts
-------
* `scripts/` Scripts that production deployments might run manually
(e.g., `restart-server`).
* `scripts/lib/` Scripts that are needed on production deployments but
humans should never run.
* `scripts/setup/` Tools that production deployments will only run
once, during installation.
* `tools/` Development tools.
---------------------------------------------------------
Bots
----
* `api/integrations/` Bots distributed as part of the Zulip API bundle.
* `bots/` Previously Zulip internal bots. These usually need a bit of
work.
-----------------------------------------------------
Management commands
-------------------
* `zerver/management/commands/` Management commands one might run at a
production deployment site (e.g. scripts to change a value or
deactivate a user properly)
-------------------------------------------------------------------------
Views
-----
* `zerver/tornadoviews.py` Tornado views
* `zerver/views/webhooks.py` Webhook views
* `zerver/views/messages.py` message-related views
* `zerver/views/__init__.py` other Django views
----------------------------------------
Jinja2 Compatibility Files
--------------------------
* `zproject/jinja2/__init__.py` Jinja2 environment
* `zproject/jinja2/backends.py` Jinja2 backend
* `zproject/jinja2/compressors.py` Jinja2 compatible functions of
Django-Pipeline
-----------------------------------------------------------------------
Static assets
-------------
* `assets/` For assets not to be served to the web (e.g. the system to
generate our favicons)
* `static/` For things we do want to both serve to the web and
distribute to production deployments (e.g. the webpages)
---------------------------------------------------------------
Puppet
------
* `puppet/zulip/` For configuration for production deployments
-------------------------------------------------------------------
Templates
---------
* `templates/zerver/` For Jinja2 templates for the backend (for zerver app)
* `static/templates/` Handlebars templates for the frontend
-----------------------------------------------------------------------
Tests
-----
* `zerver/tests/` Backend tests
* `frontend_tests/node_tests/` Node Frontend unit tests
* `frontend_tests/casper_tests/` Casper frontend tests
-----------------------------------------------------------------------
Documentation
-------------
* `docs/` Source for this documentation
--------------------------------------------------------------
You can consult the repository's `.gitattributes` file to see exactly
which components are excluded from production releases (release
tarballs are generated using `tools/build-release-tarball`).

View File

@@ -1,95 +0,0 @@
===================
Directory structure
===================
This page documents the Zulip directory structure and how to decide where to
put a file.
Scripts
=======
+--------------------+-----------------------------------------------------------------------------------+
| ``scripts/`` | Scripts that production deployments might run manually (e.g. ``restart-server``) |
+--------------------+-----------------------------------------------------------------------------------+
| ``bin/`` | Scripts that are needed on production deployments but humans should never run |
+--------------------+-----------------------------------------------------------------------------------+
| ``scripts/setup/`` | Tools that production deployments will only run once, during installation |
+--------------------+-----------------------------------------------------------------------------------+
| ``tools/`` | Development tools |
+--------------------+-----------------------------------------------------------------------------------+
Bots
====
+------------------------+----------------------------------------------------------------------+
| ``api/integrations`` | Bots distributed as part of the Zulip API bundle. |
+------------------------+----------------------------------------------------------------------+
| ``bots/`` | Previously Zulip internal bots. These usually need a bit of work. |
+------------------------+----------------------------------------------------------------------+
Management commands
===================
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------+
| ``zerver/management/commands/`` | Management commands one might run at a production deployment site (e.g. scripts to change a value or deactivate a user properly) |
+-------------------------------------+------------------------------------------------------------------------------------------------------------------------------------+
Views
=====
+--------------------------------+-----------------------------------------+
| ``zerver/tornadoviews.py`` | Tornado views |
+--------------------------------+-----------------------------------------+
| ``zerver/views/webhooks.py`` | Webhook views |
+--------------------------------+-----------------------------------------+
| ``zerver/views/messages.py`` | message-related views |
+--------------------------------+-----------------------------------------+
| ``zerver/views/__init__.py`` | other Django views |
+--------------------------------+-----------------------------------------+
Static assets
=============
+---------------+---------------------------------------------------------------------------------------------------------------+
| ``assets/`` | For assets not to be served to the web (e.g. the system to generate our favicons) |
+---------------+---------------------------------------------------------------------------------------------------------------+
| ``static/`` | For things we do want to both serve to the web and distribute to production deployments (e.g. the webpages) |
+---------------+---------------------------------------------------------------------------------------------------------------+
Puppet
======
+--------------------+----------------------------------------------------------------------------------+
| ``puppet/zulip`` | For configuration for production deployments |
+--------------------+----------------------------------------------------------------------------------+
Templates
=========
+--------------------------+--------------------------------------------------------+
| ``templates/zerver`` | For templates related to zerver views |
+--------------------------+--------------------------------------------------------+
| ``static/templates`` | Handlebars templates for the frontend |
+--------------------------+--------------------------------------------------------+
Tests
=====
+------------------------+-----------------------------------+
| ``zerver/test*.py`` | Backend tests |
+------------------------+-----------------------------------+
| ``frontend_tests/node`` | Node Frontend unit tests |
+------------------------+-----------------------------------+
| ``frontend_tests/tests`` | Casper frontend tests |
+------------------------+-----------------------------------+
Documentation
=============
+-------------+-----------------------------------------------+
| ``docs/`` | Source for this documentation |
+-------------+-----------------------------------------------+
You can consult the repository's .gitattributes file to see exactly
which components are excluded from production releases (release
tarballs are generated using tools/build-release-tarball).

View File

@@ -0,0 +1,73 @@
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,26 +0,0 @@
=======================
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 exisiting 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) 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.

View File

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

BIN
docs/images/zulip-dev.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -6,16 +6,35 @@
Welcome to Zulip documentation!
===============================
Zulip is a powerful, open source group chat application. Written in
Python and using the Django framework, Zulip supports both private
messaging and group chats via conversation streams.
Zulip also supports fast search, drag-and-drop file uploads, image
previews, group private messages, audible notifications, 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 <https://www.zulip.org>`__ and in these
docs. Our code is available at `our GitHub repository
<https://github.com/zulip/>`__.
This set of documents covers installation and contribution instructions.
Contents:
.. toctree::
:maxdepth: 2
:maxdepth: 3
readme-symlink
architecture-overview
integration-guide
new-feature-tutorial
code-style
front-end-build-process
code-contribution-checklist
directory-structure
testing
translating
changelog
roadmap
Indices and tables
==================

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

@@ -0,0 +1,182 @@
# How to write a new integration
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 is:
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.
* Consistently capitalize the name of the integration in the
documentation and the Client name the way the vendor does. It's OK
to use all-lower-case in the implementation.
* 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.

24
docs/logging.md Normal file
View File

@@ -0,0 +1,24 @@
# Logging and Performance Debugging
It's good to have the terminal running `run-dev.py` up as you work since error
messages including tracebacks along with every backend request will be printed
there.
The messages will look similar to:
```
2016-05-20 14:50:22,056 INFO 127.0.0.1 GET 302 528ms (db: 1ms/1q) (+start: 123ms) / (unauth via ?)
[20/May/2016 14:50:22]"GET / HTTP/1.0" 302 0
2016-05-20 14:50:22,272 INFO 127.0.0.1 GET 200 124ms (db: 3ms/2q) /login/ (unauth via ?)
2016-05-20 14:50:26,333 INFO 127.0.0.1 POST 302 37ms (db: 6ms/7q) /accounts/login/local/ (unauth via ?)
[20/May/2016 14:50:26]"POST /accounts/login/local/ HTTP/1.0" 302 0
2016-05-20 14:50:26,538 INFO 127.0.0.1 GET 200 12ms (db: 1ms/2q) (+start: 53ms) /api/v1/events [1463769771:0/0] (cordelia@zulip.com via internal)
2016-05-20 14:50:26,657 INFO 127.0.0.1 GET 200 10ms (+start: 8ms) /api/v1/events [1463769771:0/0] (cordelia@zulip.com via internal)
2016-05-20 14:50:26,959 INFO 127.0.0.1 GET 200 588ms (db: 26ms/21q) / [1463769771:0] (cordelia@zulip.com via website)
```
The format of this output is: timestamp, loglevel, IP, HTTP Method, HTTP status
code, time to process, (optional perf data details, e.g. database time/queries,
memcached time/queries, Django process startup time, markdown processing time,
etc.), URL, and "email via client" showing user account involved (if logged in)
and the type of client they used ("web", "Android", etc.).

136
docs/markdown.md Normal file
View File

@@ -0,0 +1,136 @@
# Zulip's Markdown implementation
Zulip has a special flavor of Markdown, currently called 'bugdown'
after Zulip's original name of "humbug". End users are using Bugdown
within the client, not original Markdown.
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 `-` (previously
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.

165
docs/mypy.md Normal file
View File

@@ -0,0 +1,165 @@
# Testing with the 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:
* [The mypy cheat
sheet](https://github.com/python/mypy/blob/master/docs/source/cheat_sheet.rst)
is the best resource for quickly understanding how to write the PEP
484 type annotations used by mypy correctly.
* The [Python 2 type annotation syntax spec 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.
## Zulip goals
Zulip is hoping to reach 100% of the codebase annotated with mypy
static types, and then enforce that it stays that way. Our current
coverage is shown in
[Coveralls](https://coveralls.io/github/zulip/zulip).
## 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 so that they pass without
errors, please remove them from the exclude list.
## Mypy is there to find bugs in Zulip before they impact users
For the purposes of Zulip development, you can treat `mypy` like a
much more powerful linter that can catch a wide range of bugs. If,
after running tools/run-mypy on your Zulip branch, you get mypy
errors, it's important to get to the bottom of the issue, not just do
something quick to silence the warnings. Possible explanations include:
* A bug in any new type annotations you added.
* A bug in the existing type annotations.
* A bug in Zulip!
* Some Zulip code is correct but confusingly reuses variables with
different types.
* A bug in mypy (though this is increasingly rare as mypy is now
fairly mature as a project).
Each explanation has its own solution, but in every case the result
should be solving the mypy warning in a way that makes the Zulip
codebase better. If you need help understanding an issue, please feel
free to, mention @sharmaeklavya2 or @timabbott on the relevant pull
request or issue on GitHub.
If you think you have found a bug in Zulip or mypy, inform the zulip
developers by opening an issue on [Zulip's github
repository](https://github.com/zulip/zulip/issues) or posting on
[zulip-devel](https://groups.google.com/d/forum/zulip-devel). If it's
indeed a mypy bug, we can help with reporting it upstream.
## Annotating strings
In python 3, strings can have non-ASCII characters without any problems.
Such characters are required to support languages which use non-latin
scripts like Japanese and Hindi. They are also needed to support special
characters like mathematical symbols, musical symbols, etc.
In python 2, however, `str` generally doesn't work well with non-ASCII
characters. That's why `unicode` was introduced in python 2.
But there are problems with the `unicode` and `str` system. Implicit
conversions between `str` and `unicode` use the `ascii` codec, which
fails on strings containing non-ASCII characters. Such errors are hard
to detect by people who always write in English. To minimize such
implicit conversions, we should have a strict separation between `str`
and `unicode` in python 2. It might seem that using `unicode` everywhere
will solve all problems, but unfortunately it doesn't. This is because
some parts of the standard library and the python language (like keyword
argument unpacking) insist that parameters passed to them are `str`.
To make our code work correctly on python 2, we have to identify strings
which contain data which could come from non-ASCII sources like stream
names, people's names, domain names, content of messages, emails, etc.
These strings should be `unicode`. We also have to identify strings
which should be `str` like Exception names, attribute names, parameter
names, etc.
Mypy can help with this. We just have to annotate each string as either
`str` or `unicode` and mypy's static type checking will tell us if we
are incorrectly mixing the two. However, `unicode` is not defined in
python 3. We want our code to be python 3 compatible in the future.
This can be achieved using 'six', a Python 2 and 3 compatibility library.
`six.text_type` is defined as `str` on python 3 and as `unicode` on
python 2. We'll be using `text_type` (instead of `unicode`) and `str`
to annotate strings in Zulip's code. We follow the style of doing
`from six import text_type` and using `text_type` for annotation instead
of doing `import six` and using `six.text_type` for annotation, because
`text_type` is used so extensively for type annotations that we don't
need to be that verbose.
Sometimes you'll find that you have to convert strings from one type to
another. `zerver/lib/str_utils.py` has utility functions to help with that.
It also has documentation (in docstrings) which explains the right way
to use them.

View File

@@ -0,0 +1,218 @@
How to write a new application feature
======================================
The changes needed to add a new feature will vary, of course, but this
document provides a general outline of what you may need to do, as well
as an example of the specific steps needed to add a new feature: adding
a new option to the application that is dynamically synced through the
data system in real-time to all browsers the user may have open.
General Process
---------------
### Adding a field to the database
**Update the model:** The server accesses the underlying database in
`zerver/ models.py`. Add a new field in the appropriate class.
**Create and run the migration:** To create and apply a migration, run:
:
./manage.py makemigrations ./manage.py migrate
**Test your changes:** Once you've run the migration, restart memcached
on your development server (`/etc/init.d/memcached restart`) and then
restart `run-dev.py` to avoid interacting with cached objects.
### Backend changes
**Database interaction:** Add any necessary code for updating and
interacting with the database in `zerver/lib/actions.py`. It should
update the database and send an event announcing the change.
**Application state:** Modify the `fetch_initial_state_data` and
`apply_events` functions in `zerver/lib/actions.py` to update the state
based on the event you just created.
**Backend implementation:** Make any other modifications to the backend
required for your change.
**Testing:** At the very least, add a test of your event data flowing
through the system in `test_events.py`.
### Frontend changes
**JavaScript:** Zulip's JavaScript is located in the directory
`static/js/`. The exact files you may need to change depend on your
feature. If you've added a new event that is sent to clients, be sure to
add a handler for it to `static/js/server_events.js`.
**CSS:** The primary CSS file is `static/styles/zulip.css`. If your new
feature requires UI changes, you may need to add additional CSS to this
file.
**Templates:** The initial page structure is rendered via Jinja2
templates located in `templates/zerver`. For JavaScript, Zulip uses
Handlebars templates located in `static/templates`. Templates are
precompiled as part of the build/deploy 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
`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 testing documentation \<testing\>.
Example Feature
---------------
This example describes the process of adding a new setting to Zulip: a
flag that restricts inviting new users to admins only (the default
behavior is that any user can invite other users). It is based on an
actual Zulip feature, and you can review [the original commit in the
Zulip git
repo](https://github.com/zulip/zulip/commit/5b7f3466baee565b8e5099bcbd3e1ccdbdb0a408).
(Note that Zulip has since been upgraded from Django 1.6 to 1.8, so the
migration format has changed.)
First, update the database and model to store the new setting. Add a new
boolean field, `realm_invite_by_admins_only`, to the Realm model in
`zerver/models.py`.
Then create a Django migration that adds a new field,
`invite_by_admins_only`, to the `zerver_realm` table.
In `zerver/lib/actions.py`, create a new function named
`do_set_realm_invite_by_admins_only`. This function will update the
database and trigger an event to notify clients when this setting
changes. In this case there was an existing `realm|update` event type
which was used for setting similar flags on the Realm model, so it was
possible to add a new property to that event rather than creating a new
one. The property name matches the database field to make it easy to
understand what it indicates.
The second argument to `send_event` is the list of users whose browser
sessions should be notified. Depending on the setting, this can be a
single user (if the setting is a personal one, like time display
format), only members in a particular stream or all active users in a
realm. :
# zerver/lib/actions.py
def do_set_realm_invite_by_admins_only(realm, invite_by_admins_only):
realm.invite_by_admins_only = invite_by_admins_only
realm.save(update_fields=['invite_by_admins_only'])
event = dict(
type="realm",
op="update",
property='invite_by_admins_only',
value=invite_by_admins_only,
)
send_event(event, active_user_ids(realm))
return {}
You then need to add code that will handle the event and update the
application state. In `zerver/lib/actions.py` update the
`fetch_initial_state` and `apply_events` functions. :
def fetch_initial_state_data(user_profile, event_types, queue_id):
# ...
state['realm_invite_by_admins_only'] = user_profile.realm.invite_by_admins_only`
In this case you don't need to change `apply_events` because there is
already code that will correctly handle the realm update event type: :
def apply_events(state, events, user_profile):
for event in events:
# ...
elif event['type'] == 'realm':
field = 'realm_' + event['property']
state[field] = event['value']
You then need to add a view for clients to access that will call the
newly-added `actions.py` code to update the database. This example
feature adds a new parameter that should be sent to clients when the
application loads and be accessible via JavaScript, and there is already
a view that does this for related flags: `update_realm`. So in this
case, we can add out code to the existing view instead of creating a
new one. :
# zerver/views/__init__.py
def home(request):
# ...
page_params = dict(
# ...
realm_invite_by_admins_only = register_ret['realm_invite_by_admins_only'],
# ...
)
Since this feature also adds a checkbox to the admin page, and adds a
new property the Realm model that can be modified from there, you also
need to make changes to the `update_realm` function in the same file: :
# zerver/views/__init__.py
def update_realm(request, user_profile,
name=REQ(validator=check_string, default=None),
restricted_to_domain=REQ(validator=check_bool, default=None),
invite_by_admins_only=REQ(validator=check_bool,default=None)):
# ...
if invite_by_admins_only is not None and
realm.invite_by_admins_only != invite_by_admins_only:
do_set_realm_invite_by_admins_only(realm, invite_by_admins_only)
data['invite_by_admins_only'] = invite_by_admins_only
Then make the required front end changes: in this case a checkbox needs
to be added to the admin page (and its value added to the data sent back
to server when a realm is updated) and the change event needs to be
handled on the client.
To add the checkbox to the admin page, modify the relevant template,
`static/templates/admin_tab.handlebars` (omitted here since it is
relatively straightforward). Then add code to handle changes to the new
form control in `static/js/admin.js`. :
var url = "/json/realm";
var new_invite_by_admins_only =
$("#id_realm_invite_by_admins_only").prop("checked");
data[invite_by_admins_only] = JSON.stringify(new_invite_by_admins_only);
channel.patch({
url: url,
data: data,
success: function (data) {
# ...
if (data.invite_by_admins_only) {
ui.report_success("New users must be invited by an admin!", invite_by_admins_only_status);
} else {
ui.report_success("Any user may now invite new users!", invite_by_admins_only_status);
}
# ...
}
});
Finally, update `server_events.js` to handle related events coming from
the server. :
# static/js/server_events.js
function get_events_success(events) {
# ...
var dispatch_event = function dispatch_event(event) {
switch (event.type) {
# ...
case 'realm':
if (event.op === 'update' && event.property === 'invite_by_admins_only') {
page_params.realm_invite_by_admins_only = event.value;
}
}
}
Any code needed to update the UI should be placed in `dispatch_event`
callback (rather than the `channel.patch`) function. This ensures the
appropriate code will run even if the changes are made in another
browser window. In this example most of the changes are on the backend,
so no UI updates are required.

View File

@@ -1,216 +0,0 @@
====================
New Feature Tutorial
====================
The changes needed to add a new feature will vary, of course, but this document
provides a general outline of what you may need to do, as well as an example of
the specific steps needed to add a new feature: adding a new option to the
application that is dynamically synced through the data system in real-time to
all browsers the user may have open.
General Process
===============
Adding a field to the database
------------------------------
**Update the model:** The server accesses the underlying database in `zerver/
models.py`. Add a new field in the appropriate class.
**Create and run the migration:** To create and apply a migration, run: ::
./manage.py makemigrations
./manage.py migrate
**Test your changes:** Once you've run the migration, restart memcached on your
development server (``/etc/init.d/memcached restart``) and then restart
``run-dev.py`` to avoid interacting with cached objects.
Backend changes
---------------
**Database interaction:** Add any necessary code for updating and interacting
with the database in ``zerver/lib/actions.py``. It should update the database and
send an event announcing the change.
**Application state:** Modify the ``fetch_initial_state_data`` and ``apply_events``
functions in ``zerver/lib/actions.py`` to update the state based on the event you
just created.
**Backend implementation:** Make any other modifications to the backend required for
your change.
**Testing:** At the very least, add a test of your event data flowing through
the system in ``test_events.py``.
Frontend changes
----------------
**JavaScript:** Zulip's JavaScript is located in the directory ``static/js/``.
The exact files you may need to change depend on your feature. If you've added a
new event that is sent to clients, be sure to add a handler for it to
``static/js/server_events.js``.
**CSS:** The primary CSS file is ``static/styles/zulip.css``. If your new
feature requires UI changes, you may need to add additional CSS to this file.
**Templates:** The initial page structure is rendered via Django templates
located in ``template/server``. For JavaScript, Zulip uses Handlebars templates located in
``static/templates``. Templates are precompiled as part of the build/deploy
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/``.
For more information on writing and running tests see the :doc:`testing
documentation <testing>`.
Example Feature
===============
This example describes the process of adding a new setting to Zulip:
a flag that restricts inviting new users to admins only (the default behavior
is that any user can invite other users). It is based on an actual Zulip feature,
and you can review `the original commit in the Zulip git repo <https://github.com/zulip/zulip/commit/5b7f3466baee565b8e5099bcbd3e1ccdbdb0a408>`_.
(Note that Zulip has since been upgraded from Django 1.6 to 1.8, so the migration
format has changed.)
First, update the database and model to store the new setting. Add a
new boolean field, ``realm_invite_by_admins_only``, to the Realm model in
``zerver/models.py``.
Then create a Django migration that adds a new field, ``invite_by_admins_only``,
to the ``zerver_realm`` table.
In ``zerver/lib/actions.py``, create a new function named
``do_set_realm_invite_by_admins_only``. This function will update the database
and trigger an event to notify clients when this setting changes. In this case
there was an exisiting ``realm|update`` event type which was used for setting
similar flags on the Realm model, so it was possible to add a new property to
that event rather than creating a new one. The property name matches the
database field to make it easy to understand what it indicates.
The second argument to ``send_event`` is the list of users whose browser
sessions should be notified. Depending on the setting, this can be a single user
(if the setting is a personal one, like time display format), only members in a
particular stream or all active users in a realm. ::
# zerver/lib/actions.py
def do_set_realm_invite_by_admins_only(realm, invite_by_admins_only):
realm.invite_by_admins_only = invite_by_admins_only
realm.save(update_fields=['invite_by_admins_only'])
event = dict(
type="realm",
op="update",
property='invite_by_admins_only',
value=invite_by_admins_only,
)
send_event(event, active_user_ids(realm))
return {}
You then need to add code that will handle the event and update the application
state. In ``zerver/lib/actions.py`` update the ``fetch_initial_state`` and
``apply_events`` functions. ::
def fetch_initial_state_data(user_profile, event_types, queue_id):
# ...
state['realm_invite_by_admins_only'] = user_profile.realm.invite_by_admins_only`
In this case you don't need to change ``apply_events`` because there is already
code that will correctly handle the realm update event type: ::
def apply_events(state, events, user_profile):
for event in events:
# ...
elif event['type'] == 'realm':
field = 'realm_' + event['property']
state[field] = event['value']
You then need to add a view for clients to access that will call the newly-added
``actions.py`` code to update the database. This example feature adds a new
parameter that should be sent to clients when the application loads and be
accessible via JavaScript, and there is already a view that does this for
related flags: ``update_realm``. So in this case, we can add out code to the
exisiting view instead of creating a new one. ::
# zerver/views/__init__.py
def home(request):
# ...
page_params = dict(
# ...
realm_invite_by_admins_only = register_ret['realm_invite_by_admins_only'],
# ...
)
Since this feature also adds a checkbox to the admin page, and adds a new
property the Realm model that can be modified from there, you also need to make
changes to the ``update_realm`` function in the same file: ::
# zerver/views/__init__.py
def update_realm(request, user_profile,
name=REQ(validator=check_string, default=None),
restricted_to_domain=REQ(validator=check_bool, default=None),
invite_by_admins_only=REQ(validator=check_bool,default=None)):
# ...
if invite_by_admins_only is not None and
realm.invite_by_admins_only != invite_by_admins_only:
do_set_realm_invite_by_admins_only(realm, invite_by_admins_only)
data['invite_by_admins_only'] = invite_by_admins_only
Then make the required front end changes: in this case a checkbox needs to be
added to the admin page (and its value added to the data sent back to server
when a realm is updated) and the change event needs to be handled on the client.
To add the checkbox to the admin page, modify the relevant template,
``static/templates/admin_tab.handlebars`` (omitted here since it is relatively
straightforward). Then add code to handle changes to the new form control in
``static/js/admin.js``. ::
var url = "/json/realm";
var new_invite_by_admins_only =
$("#id_realm_invite_by_admins_only").prop("checked");
data[invite_by_admins_only] = JSON.stringify(new_invite_by_admins_only);
channel.patch({
url: url,
data: data,
success: function (data) {
# ...
if (data.invite_by_admins_only) {
ui.report_success("New users must be invited by an admin!", invite_by_admins_only_status);
} else {
ui.report_success("Any user may now invite new users!", invite_by_admins_only_status);
}
# ...
}
});
Finally, update ``server_events.js`` to handle related events coming from the
server. ::
# static/js/server_events.js
function get_events_success(events) {
# ...
var dispatch_event = function dispatch_event(event) {
switch (event.type) {
# ...
case 'realm':
if (event.op === 'update' && event.property === 'invite_by_admins_only') {
page_params.realm_invite_by_admins_only = event.value;
}
}
}
Any code needed to update the UI should be placed in ``dispatch_event`` callback
(rather than the ``channel.patch``) function. This ensures the appropriate code
will run even if the changes are made in another browser window. In this example
most of the changes are on the backend, so no UI updates are required.

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`.

1
docs/readme-symlink.md Symbolic link
View File

@@ -0,0 +1 @@
../README.md

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