def update_stream_backend( request: HttpRequest, user_profile: UserProfile, stream_id: int, description: Optional[Text]=REQ(validator=check_string, default=None), is_private: Optional[bool]=REQ(validator=check_bool, default=None), new_name: Optional[Text]=REQ(validator=check_string, default=None), ) -> HttpResponse: # We allow realm administrators to to update the stream name and # description even for private streams. stream = access_stream_for_delete_or_update(user_profile, stream_id) if description is not None: do_change_stream_description(stream, description) if new_name is not None: new_name = new_name.strip() if stream.name == new_name: return json_error(_("Stream already has that name!")) if stream.name.lower() != new_name.lower(): # Check that the stream name is available (unless we are # are only changing the casing of the stream name). check_stream_name_available(user_profile.realm, new_name) do_rename_stream(stream, new_name) # But we require even realm administrators to be actually # subscribed to make a private stream public. if is_private is not None: (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id) do_change_stream_invite_only(stream, is_private) return json_success()
def test_add_bot_with_default_sending_stream_private_denied(self) -> None: self.login(self.example_email('hamlet')) realm = self.example_user('hamlet').realm stream = get_stream("Denmark", realm) self.unsubscribe(self.example_user('hamlet'), "Denmark") do_change_stream_invite_only(stream, True) bot_info = { 'full_name': 'The Bot of Hamlet', 'short_name': 'hambot', 'default_sending_stream': 'Denmark', } result = self.client_post("/json/bots", bot_info) self.assert_json_error(result, "Invalid stream name 'Denmark'")
def test_patch_bot_events_register_stream_denied(self) -> None: self.login(self.example_email('hamlet')) realm = self.example_user('hamlet').realm stream = get_stream("Denmark", realm) self.unsubscribe(self.example_user('hamlet'), "Denmark") do_change_stream_invite_only(stream, True) bot_info = { 'full_name': 'The Bot of Hamlet', 'short_name': 'hambot', } result = self.client_post("/json/bots", bot_info) self.assert_json_success(result) bot_info = { 'default_events_register_stream': 'Denmark', } result = self.client_patch("/json/bots/[email protected]", bot_info) self.assert_json_error(result, "Invalid stream name 'Denmark'")
def update_stream_backend(request, user_profile, stream_id, description=REQ(validator=check_string, default=None), is_private=REQ(validator=check_bool, default=None), new_name=REQ(validator=check_string, default=None)): # type: (HttpRequest, UserProfile, int, Optional[Text], Optional[bool], Optional[Text]) -> HttpResponse (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id) if description is not None: do_change_stream_description(stream, description) if new_name is not None: new_name = new_name.strip() if stream.name == new_name: return json_error(_("Stream already has that name!")) if stream.name.lower() != new_name.lower(): # Check that the stream name is available (unless we are # are only changing the casing of the stream name). check_stream_name_available(user_profile.realm, new_name) do_rename_stream(stream, new_name) if is_private is not None: do_change_stream_invite_only(stream, is_private) return json_success()
def test_patch_bot_events_register_stream_allowed(self) -> None: self.login(self.example_email('hamlet')) user_profile = self.example_user('hamlet') stream = self.subscribe(user_profile, "Denmark") do_change_stream_invite_only(stream, True) bot_info = { 'full_name': 'The Bot of Hamlet', 'short_name': 'hambot', } result = self.client_post("/json/bots", bot_info) self.assert_json_success(result) bot_info = { 'default_events_register_stream': 'Denmark', } result = self.client_patch("/json/bots/[email protected]", bot_info) self.assert_json_success(result) self.assertEqual('Denmark', result.json()['default_events_register_stream']) bot = self.get_bot() self.assertEqual('Denmark', bot['default_events_register_stream'])
def test_add_bot_with_default_sending_stream_private_allowed(self) -> None: self.login(self.example_email('hamlet')) user_profile = self.example_user('hamlet') stream = get_stream("Denmark", user_profile.realm) self.subscribe(user_profile, stream.name) do_change_stream_invite_only(stream, True) self.assert_num_bots_equal(0) events = [] # type: List[Mapping[str, Any]] with tornado_redirected_to_list(events): result = self.create_bot(default_sending_stream='Denmark') self.assert_num_bots_equal(1) self.assertEqual(result['default_sending_stream'], 'Denmark') email = '*****@*****.**' realm = get_realm('zulip') profile = get_user(email, realm) self.assertEqual(profile.default_sending_stream.name, 'Denmark') event = [e for e in events if e['event']['type'] == 'realm_bot'][0] self.assertEqual( dict( type='realm_bot', op='add', bot=dict(email='*****@*****.**', user_id=profile.id, full_name='The Bot of Hamlet', bot_type=profile.bot_type, is_active=True, api_key=result['api_key'], avatar_url=result['avatar_url'], default_sending_stream='Denmark', default_events_register_stream=None, default_all_public_streams=False, owner=self.example_email('hamlet')) ), event['event'] ) self.assertEqual(event['users'], {user_profile.id, })
def test_add_bot_with_default_sending_stream_private_allowed(self) -> None: self.login(self.example_email('hamlet')) user_profile = self.example_user('hamlet') stream = get_stream("Denmark", user_profile.realm) self.subscribe(user_profile, stream.name) do_change_stream_invite_only(stream, True) self.assert_num_bots_equal(0) events = [] # type: List[Mapping[str, Any]] with tornado_redirected_to_list(events): result = self.create_bot(default_sending_stream='Denmark') self.assert_num_bots_equal(1) self.assertEqual(result['default_sending_stream'], 'Denmark') email = '*****@*****.**' realm = get_realm('zulip') profile = get_user(email, realm) self.assertEqual(profile.default_sending_stream.name, 'Denmark') event = [e for e in events if e['event']['type'] == 'realm_bot'][0] self.assertEqual( dict(type='realm_bot', op='add', bot=dict(email='*****@*****.**', user_id=profile.id, full_name='The Bot of Hamlet', bot_type=profile.bot_type, is_active=True, api_key=result['api_key'], avatar_url=result['avatar_url'], default_sending_stream='Denmark', default_events_register_stream=None, default_all_public_streams=False, owner=self.example_email('hamlet'))), event['event']) self.assertEqual(event['users'], { user_profile.id, })
def update_stream_backend( request: HttpRequest, user_profile: UserProfile, stream_id: int, description: Optional[str]=REQ(validator=check_capped_string( Stream.MAX_DESCRIPTION_LENGTH), default=None), is_private: Optional[bool]=REQ(validator=check_bool, default=None), is_announcement_only: Optional[bool]=REQ(validator=check_bool, default=None), history_public_to_subscribers: Optional[bool]=REQ(validator=check_bool, default=None), new_name: Optional[str]=REQ(validator=check_string, default=None), ) -> HttpResponse: # We allow realm administrators to to update the stream name and # description even for private streams. stream = access_stream_for_delete_or_update(user_profile, stream_id) if description is not None: if '\n' in description: # We don't allow newline characters in stream descriptions. description = description.replace("\n", " ") do_change_stream_description(stream, description) if new_name is not None: new_name = new_name.strip() if stream.name == new_name: return json_error(_("Stream already has that name!")) if stream.name.lower() != new_name.lower(): # Check that the stream name is available (unless we are # are only changing the casing of the stream name). check_stream_name_available(user_profile.realm, new_name) do_rename_stream(stream, new_name, user_profile) if is_announcement_only is not None: do_change_stream_announcement_only(stream, is_announcement_only) # But we require even realm administrators to be actually # subscribed to make a private stream public. if is_private is not None: (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id) do_change_stream_invite_only(stream, is_private, history_public_to_subscribers) return json_success()
def update_stream_backend( request: HttpRequest, user_profile: UserProfile, stream_id: int, description: Optional[str] = REQ(str_validator=check_capped_string( Stream.MAX_DESCRIPTION_LENGTH), default=None), is_private: Optional[bool] = REQ(json_validator=check_bool, default=None), is_announcement_only: Optional[bool] = REQ(json_validator=check_bool, default=None), stream_post_policy: Optional[int] = REQ(json_validator=check_int_in( Stream.STREAM_POST_POLICY_TYPES), default=None), history_public_to_subscribers: Optional[bool] = REQ( json_validator=check_bool, default=None), new_name: Optional[str] = REQ(default=None), message_retention_days: Optional[Union[int, str]] = REQ( json_validator=check_string_or_int, default=None), ) -> HttpResponse: # We allow realm administrators to to update the stream name and # description even for private streams. (stream, sub) = access_stream_for_delete_or_update(user_profile, stream_id) if message_retention_days is not None: if not user_profile.is_realm_owner: raise OrganizationOwnerRequired() user_profile.realm.ensure_not_on_limited_plan() message_retention_days_value = parse_message_retention_days( message_retention_days, Stream.MESSAGE_RETENTION_SPECIAL_VALUES_MAP) do_change_stream_message_retention_days(stream, message_retention_days_value) if description is not None: if "\n" in description: # We don't allow newline characters in stream descriptions. description = description.replace("\n", " ") do_change_stream_description(stream, description) if new_name is not None: new_name = new_name.strip() if stream.name == new_name: return json_error(_("Stream already has that name!")) if stream.name.lower() != new_name.lower(): # Check that the stream name is available (unless we are # are only changing the casing of the stream name). check_stream_name_available(user_profile.realm, new_name) do_rename_stream(stream, new_name, user_profile) if is_announcement_only is not None: # is_announcement_only is a legacy way to specify # stream_post_policy. We can probably just delete this code, # since we're not aware of clients that used it, but we're # keeping it for backwards-compatibility for now. stream_post_policy = Stream.STREAM_POST_POLICY_EVERYONE if is_announcement_only: stream_post_policy = Stream.STREAM_POST_POLICY_ADMINS if stream_post_policy is not None: do_change_stream_post_policy(stream, stream_post_policy) # But we require even realm administrators to be actually # subscribed to make a private stream public. if is_private is not None: default_stream_ids = { s.id for s in get_default_streams_for_realm(stream.realm_id) } (stream, sub) = access_stream_by_id(user_profile, stream_id) if is_private and stream.id in default_stream_ids: return json_error(_("Default streams cannot be made private.")) do_change_stream_invite_only(stream, is_private, history_public_to_subscribers) return json_success()
def test_reaction_event_scope(self) -> None: iago = self.example_user("iago") hamlet = self.example_user("hamlet") polonius = self.example_user("polonius") reaction_info = { "emoji_name": "smile", } # Test `invite_only` streams with `!history_public_to_subscribers` and `!is_web_public` stream = self.make_stream("test_reactions_stream", invite_only=True, history_public_to_subscribers=False) self.subscribe(iago, stream.name) message_before_id = self.send_stream_message( iago, "test_reactions_stream", "before subscription history private") self.subscribe(hamlet, stream.name) self.subscribe(polonius, stream.name) # Hamlet and Polonius joined after the message was sent, and # so only Iago should receive the event. events: List[Mapping[str, Any]] = [] with tornado_redirected_to_list(events): result = self.api_post( iago, f"/api/v1/messages/{message_before_id}/reactions", reaction_info) self.assert_json_success(result) self.assert_length(events, 1) event = events[0]["event"] self.assertEqual(event["type"], "reaction") event_user_ids = set(events[0]["users"]) self.assertEqual(event_user_ids, {iago.id}) remove = self.api_delete( iago, f"/api/v1/messages/{message_before_id}/reactions", reaction_info) self.assert_json_success(remove) # Reaction to a Message sent after subscription, should # trigger events for all subscribers (Iago, Hamlet and Polonius). message_after_id = self.send_stream_message( iago, "test_reactions_stream", "after subscription history private") events = [] with tornado_redirected_to_list(events): result = self.api_post( iago, f"/api/v1/messages/{message_after_id}/reactions", reaction_info) self.assert_json_success(result) self.assert_length(events, 1) event = events[0]["event"] self.assertEqual(event["type"], "reaction") event_user_ids = set(events[0]["users"]) self.assertEqual(event_user_ids, {iago.id, hamlet.id, polonius.id}) remove = self.api_delete( iago, f"/api/v1/messages/{message_after_id}/reactions", reaction_info) self.assert_json_success(remove) # Make stream history public to subscribers do_change_stream_invite_only(stream, False, history_public_to_subscribers=True) # Since stream history is public to subscribers, reacting to # message_before_id should notify all subscribers: # Iago and Hamlet. events = [] with tornado_redirected_to_list(events): result = self.api_post( iago, f"/api/v1/messages/{message_before_id}/reactions", reaction_info) self.assert_json_success(result) self.assert_length(events, 1) event = events[0]["event"] self.assertEqual(event["type"], "reaction") event_user_ids = set(events[0]["users"]) self.assertEqual(event_user_ids, {iago.id, hamlet.id, polonius.id}) remove = self.api_delete( iago, f"/api/v1/messages/{message_before_id}/reactions", reaction_info) self.assert_json_success(remove) # Make stream web_public as well. do_make_stream_web_public(stream) # For is_web_public streams, events even on old messages # should go to all subscribers, including guests like polonius. events = [] with tornado_redirected_to_list(events): result = self.api_post( iago, f"/api/v1/messages/{message_before_id}/reactions", reaction_info) self.assert_json_success(result) self.assert_length(events, 1) event = events[0]["event"] self.assertEqual(event["type"], "reaction") event_user_ids = set(events[0]["users"]) self.assertEqual(event_user_ids, {iago.id, hamlet.id, polonius.id}) remove = self.api_delete( iago, f"/api/v1/messages/{message_before_id}/reactions", reaction_info) self.assert_json_success(remove) # Private message, event should go to both participants. private_message_id = self.send_personal_message( iago, hamlet, "hello to single receiver", ) events = [] with tornado_redirected_to_list(events): result = self.api_post( hamlet, f"/api/v1/messages/{private_message_id}/reactions", reaction_info) self.assert_json_success(result) self.assert_length(events, 1) event = events[0]["event"] self.assertEqual(event["type"], "reaction") event_user_ids = set(events[0]["users"]) self.assertEqual(event_user_ids, {iago.id, hamlet.id}) # Group private message; event should go to all participants. huddle_message_id = self.send_huddle_message( hamlet, [polonius, iago], "hello message to muliple receiver", ) events = [] with tornado_redirected_to_list(events): result = self.api_post( polonius, f"/api/v1/messages/{huddle_message_id}/reactions", reaction_info) self.assert_json_success(result) self.assert_length(events, 1) event = events[0]["event"] self.assertEqual(event["type"], "reaction") event_user_ids = set(events[0]["users"]) self.assertEqual(event_user_ids, {iago.id, hamlet.id, polonius.id})
def test_topics_history(self) -> None: # verified: int(UserMessage.flags.read) == 1 user_profile = self.example_user("iago") self.login_user(user_profile) stream_name = "Verona" stream = get_stream(stream_name, user_profile.realm) recipient = stream.recipient def create_test_message(topic: str) -> int: # TODO: Clean this up to send messages the normal way. hamlet = self.example_user("hamlet") message = Message( sender=hamlet, recipient=recipient, content="whatever", date_sent=timezone_now(), sending_client=get_client("whatever"), ) message.set_topic_name(topic) message.save() UserMessage.objects.create( user_profile=user_profile, message=message, flags=0, ) return message.id # our most recent topics are topic0, topic1, topic2 # Create old messages with strange spellings. create_test_message("topic2") create_test_message("toPIc1") create_test_message("toPIc0") create_test_message("topic2") create_test_message("topic2") create_test_message("Topic2") # Create new messages topic2_msg_id = create_test_message("topic2") create_test_message("topic1") create_test_message("topic1") topic1_msg_id = create_test_message("topic1") topic0_msg_id = create_test_message("topic0") endpoint = f"/json/users/me/{stream.id}/topics" result = self.client_get(endpoint, {}) self.assert_json_success(result) history = result.json()["topics"] # We only look at the most recent three topics, because # the prior fixture data may be unreliable. history = history[:3] self.assertEqual( [topic["name"] for topic in history], [ "topic0", "topic1", "topic2", ], ) self.assertEqual( [topic["max_id"] for topic in history], [ topic0_msg_id, topic1_msg_id, topic2_msg_id, ], ) # Now try as cordelia, who we imagine as a totally new user in # that she doesn't have UserMessage rows. We should see the # same results for a public stream. self.login("cordelia") result = self.client_get(endpoint, {}) self.assert_json_success(result) history = result.json()["topics"] # We only look at the most recent three topics, because # the prior fixture data may be unreliable. history = history[:3] self.assertEqual( [topic["name"] for topic in history], [ "topic0", "topic1", "topic2", ], ) self.assertIn("topic0", [topic["name"] for topic in history]) self.assertEqual( [topic["max_id"] for topic in history], [ topic0_msg_id, topic1_msg_id, topic2_msg_id, ], ) # Now make stream private, but subscribe cordelia do_change_stream_invite_only(stream, True) self.subscribe(self.example_user("cordelia"), stream.name) result = self.client_get(endpoint, {}) self.assert_json_success(result) history = result.json()["topics"] history = history[:3] # Cordelia doesn't have these recent history items when we # wasn't subscribed in her results. self.assertNotIn("topic0", [topic["name"] for topic in history]) self.assertNotIn("topic1", [topic["name"] for topic in history]) self.assertNotIn("topic2", [topic["name"] for topic in history])
def test_topic_delete(self) -> None: initial_last_msg_id = self.get_last_message().id stream_name = "new_stream" topic_name = "new topic 2" # NON-ADMIN USER user_profile = self.example_user("hamlet") self.subscribe(user_profile, stream_name) # Send message stream = get_stream(stream_name, user_profile.realm) self.send_stream_message(user_profile, stream_name, topic_name=topic_name) last_msg_id = self.send_stream_message(user_profile, stream_name, topic_name=topic_name) # Deleting the topic self.login_user(user_profile) endpoint = "/json/streams/" + str(stream.id) + "/delete_topic" result = self.client_post( endpoint, { "topic_name": topic_name, }, ) self.assert_json_error(result, "Must be an organization administrator") self.assertEqual(self.get_last_message().id, last_msg_id) # Make stream private with limited history do_change_stream_invite_only(stream, invite_only=True, history_public_to_subscribers=False) # ADMIN USER subscribed now user_profile = self.example_user("iago") self.subscribe(user_profile, stream_name) self.login_user(user_profile) new_last_msg_id = self.send_stream_message(user_profile, stream_name, topic_name=topic_name) # Now admin deletes all messages in topic -- which should only # delete new_last_msg_id, i.e. the one sent since they joined. self.assertEqual(self.get_last_message().id, new_last_msg_id) result = self.client_post( endpoint, { "topic_name": topic_name, }, ) self.assert_json_success(result) self.assertEqual(self.get_last_message().id, last_msg_id) # Try to delete all messages in the topic again. There are no messages accessible # to the administrator, so this should do nothing. result = self.client_post( endpoint, { "topic_name": topic_name, }, ) self.assert_json_success(result) self.assertEqual(self.get_last_message().id, last_msg_id) # Make the stream's history public to subscribers do_change_stream_invite_only(stream, invite_only=True, history_public_to_subscribers=True) # Delete the topic should now remove all messages result = self.client_post( endpoint, { "topic_name": topic_name, }, ) self.assert_json_success(result) self.assertEqual(self.get_last_message().id, initial_last_msg_id) # Delete again, to test the edge case of deleting an empty topic. result = self.client_post( endpoint, { "topic_name": topic_name, }, ) self.assert_json_success(result) self.assertEqual(self.get_last_message().id, initial_last_msg_id)