diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py index 25cd4bd617..a8ec4dce6d 100644 --- a/zerver/lib/actions.py +++ b/zerver/lib/actions.py @@ -77,6 +77,10 @@ from zerver.lib.users import ( get_api_key, user_ids_to_users ) +from zerver.lib.user_status import ( + revoke_away_status, + set_away_status, +) from zerver.lib.user_groups import create_user_group, access_user_group_by_id from zerver.models import Realm, RealmEmoji, Stream, UserProfile, UserActivity, \ @@ -3706,6 +3710,32 @@ def do_update_pointer(user_profile: UserProfile, client: Client, event = dict(type='pointer', pointer=pointer) send_event(user_profile.realm, event, [user_profile.id]) +def do_set_away_status(user_profile: UserProfile, + client_id: int) -> None: + realm = user_profile.realm + set_away_status( + user_profile_id=user_profile.id, + client_id=client_id, + ) + event = dict( + type='user_status', + user_id=user_profile.id, + away=True, + ) + send_event(realm, event, active_user_ids(realm.id)) + +def do_revoke_away_status(user_profile: UserProfile) -> None: + realm = user_profile.realm + revoke_away_status( + user_profile_id=user_profile.id, + ) + event = dict( + type='user_status', + user_id=user_profile.id, + away=False, + ) + send_event(realm, event, active_user_ids(realm.id)) + def do_mark_all_as_read(user_profile: UserProfile, client: Client) -> int: log_statsd_event('bankruptcy') diff --git a/zerver/tests/test_user_status.py b/zerver/tests/test_user_status.py index 5b84fbe2db..59e8dd84de 100644 --- a/zerver/tests/test_user_status.py +++ b/zerver/tests/test_user_status.py @@ -1,6 +1,12 @@ +import ujson + from zerver.lib.test_classes import ( ZulipTestCase, ) +from zerver.lib.test_helpers import ( + EventInfo, + capture_event, +) from zerver.lib.user_status import ( get_away_user_ids, revoke_away_status, @@ -12,6 +18,8 @@ from zerver.models import ( UserStatus, ) +from typing import Any, Dict + class UserStatusTest(ZulipTestCase): def test_basics(self) -> None: cordelia = self.example_user('cordelia') @@ -86,3 +94,46 @@ class UserStatusTest(ZulipTestCase): away_user_ids = get_away_user_ids(realm_id=realm_id) self.assertEqual(away_user_ids, {cordelia.id}) + + def test_endpoints(self) -> None: + hamlet = self.example_user('hamlet') + realm_id = hamlet.realm_id + + self.login(hamlet.email) + + # Try to omit parameter--this should be an error. + payload = dict() # type: Dict[str, Any] + result = self.client_post('/json/users/me/status', payload) + self.assert_json_error(result, "Missing 'away' argument") + + # Set the "away" status. + payload = dict(away=ujson.dumps(True)) + + event_info = EventInfo() + with capture_event(event_info): + result = self.client_post('/json/users/me/status', payload) + self.assert_json_success(result) + + self.assertEqual( + event_info.payload, + dict(type='user_status', user_id=hamlet.id, away=True), + ) + + away_user_ids = get_away_user_ids(realm_id=realm_id) + self.assertEqual(away_user_ids, {hamlet.id}) + + # Now revoke "away" status. + payload = dict(away=ujson.dumps(False)) + + event_info = EventInfo() + with capture_event(event_info): + result = self.client_post('/json/users/me/status', payload) + self.assert_json_success(result) + + self.assertEqual( + event_info.payload, + dict(type='user_status', user_id=hamlet.id, away=False), + ) + + away_user_ids = get_away_user_ids(realm_id=realm_id) + self.assertEqual(away_user_ids, set()) diff --git a/zerver/views/presence.py b/zerver/views/presence.py index 97a6021f28..827f4fa35f 100644 --- a/zerver/views/presence.py +++ b/zerver/views/presence.py @@ -10,7 +10,12 @@ from django.utils.timezone import now as timezone_now from django.utils.translation import ugettext as _ from zerver.decorator import human_users_only -from zerver.lib.actions import get_status_dict, update_user_presence +from zerver.lib.actions import ( + do_revoke_away_status, + do_set_away_status, + get_status_dict, + update_user_presence, +) from zerver.lib.request import has_request_variables, REQ, JsonableError from zerver.lib.response import json_success, json_error from zerver.lib.timestamp import datetime_to_timestamp @@ -46,6 +51,25 @@ def get_presence_backend(request: HttpRequest, user_profile: UserProfile, val.pop('pushable', None) return json_success(result) +@human_users_only +@has_request_variables +def update_user_status_backend(request: HttpRequest, + user_profile: UserProfile, + away: bool=REQ(validator=check_bool), + ) -> HttpResponse: + if away: + do_set_away_status( + user_profile=user_profile, + client_id=request.client.id, + ) + else: + do_revoke_away_status( + user_profile=user_profile, + ) + + result = dict() + return json_success(result) + @human_users_only @has_request_variables def update_active_status_backend(request: HttpRequest, user_profile: UserProfile, diff --git a/zproject/urls.py b/zproject/urls.py index 26c2489738..f2461b9f8e 100644 --- a/zproject/urls.py +++ b/zproject/urls.py @@ -251,6 +251,8 @@ v1_api_and_json_patterns = [ 'POST': 'zerver.views.pointer.update_pointer_backend'}), url(r'^users/me/presence$', rest_dispatch, {'POST': 'zerver.views.presence.update_active_status_backend'}), + url(r'^users/me/status$', rest_dispatch, + {'POST': 'zerver.views.presence.update_user_status_backend'}), # Endpoint used by mobile devices to register their push # notification credentials url(r'^users/me/apns_device_token$', rest_dispatch,