2017-04-27 22:48:06 +02:00
|
|
|
|
2018-05-10 19:13:36 +02:00
|
|
|
from typing import Iterable, List, Optional, Sequence
|
2017-04-27 22:48:06 +02:00
|
|
|
|
2017-08-18 02:15:51 +02:00
|
|
|
from django.core.exceptions import ValidationError
|
2017-04-27 22:48:06 +02:00
|
|
|
from django.utils.translation import ugettext as _
|
2017-08-18 05:01:22 +02:00
|
|
|
from zerver.lib.exceptions import JsonableError
|
2017-04-27 22:48:06 +02:00
|
|
|
from zerver.lib.request import JsonableError
|
2017-08-18 02:15:51 +02:00
|
|
|
from zerver.models import (
|
2017-09-25 23:20:44 +02:00
|
|
|
Realm,
|
2017-08-18 02:15:51 +02:00
|
|
|
UserProfile,
|
2017-08-18 12:26:43 +02:00
|
|
|
get_user_including_cross_realm,
|
2017-08-18 02:15:51 +02:00
|
|
|
)
|
|
|
|
|
2018-05-10 19:13:36 +02:00
|
|
|
def user_profiles_from_unvalidated_emails(emails: Iterable[str], realm: Realm) -> List[UserProfile]:
|
2017-08-18 02:15:51 +02:00
|
|
|
user_profiles = [] # type: List[UserProfile]
|
|
|
|
for email in emails:
|
|
|
|
try:
|
2017-09-25 21:51:47 +02:00
|
|
|
user_profile = get_user_including_cross_realm(email, realm)
|
2017-08-18 02:15:51 +02:00
|
|
|
except UserProfile.DoesNotExist:
|
|
|
|
raise ValidationError(_("Invalid email '%s'") % (email,))
|
|
|
|
user_profiles.append(user_profile)
|
|
|
|
return user_profiles
|
2017-04-27 22:48:06 +02:00
|
|
|
|
2018-05-10 19:13:36 +02:00
|
|
|
def get_user_profiles(emails: Iterable[str], realm: Realm) -> List[UserProfile]:
|
2017-08-18 05:01:22 +02:00
|
|
|
try:
|
2017-09-25 21:51:47 +02:00
|
|
|
return user_profiles_from_unvalidated_emails(emails, realm)
|
2017-08-18 05:01:22 +02:00
|
|
|
except ValidationError as e:
|
2017-09-27 10:06:17 +02:00
|
|
|
assert isinstance(e.messages[0], str)
|
2017-08-18 05:01:22 +02:00
|
|
|
raise JsonableError(e.messages[0])
|
|
|
|
|
2017-11-05 11:37:41 +01:00
|
|
|
class Addressee:
|
2017-04-27 22:48:06 +02:00
|
|
|
# This is really just a holder for vars that tended to be passed
|
|
|
|
# around in a non-type-safe way before this class was introduced.
|
|
|
|
#
|
|
|
|
# It also avoids some nonsense where you have to think about whether
|
|
|
|
# topic should be None or '' for a PM, or you have to make an array
|
|
|
|
# of one stream.
|
|
|
|
#
|
|
|
|
# Eventually we can use this to cache Stream and UserProfile objects
|
|
|
|
# in memory.
|
|
|
|
#
|
|
|
|
# This should be treated as an immutable class.
|
2017-11-05 11:15:10 +01:00
|
|
|
def __init__(self, msg_type: str,
|
|
|
|
user_profiles: Optional[Sequence[UserProfile]]=None,
|
2018-05-10 19:13:36 +02:00
|
|
|
stream_name: Optional[str]=None,
|
|
|
|
topic: Optional[str]=None) -> None:
|
2017-04-27 22:48:06 +02:00
|
|
|
assert(msg_type in ['stream', 'private'])
|
|
|
|
self._msg_type = msg_type
|
2017-08-18 05:01:22 +02:00
|
|
|
self._user_profiles = user_profiles
|
2017-04-27 22:48:06 +02:00
|
|
|
self._stream_name = stream_name
|
|
|
|
self._topic = topic
|
|
|
|
|
2017-11-05 11:15:10 +01:00
|
|
|
def is_stream(self) -> bool:
|
2017-04-27 22:48:06 +02:00
|
|
|
return self._msg_type == 'stream'
|
|
|
|
|
2017-11-05 11:15:10 +01:00
|
|
|
def is_private(self) -> bool:
|
2017-04-27 22:48:06 +02:00
|
|
|
return self._msg_type == 'private'
|
|
|
|
|
2017-11-05 11:15:10 +01:00
|
|
|
def user_profiles(self) -> List[UserProfile]:
|
2017-04-27 22:48:06 +02:00
|
|
|
assert(self.is_private())
|
2017-08-18 05:01:22 +02:00
|
|
|
return self._user_profiles # type: ignore # assertion protects us
|
2017-04-27 22:48:06 +02:00
|
|
|
|
2018-05-10 19:13:36 +02:00
|
|
|
def stream_name(self) -> str:
|
2017-04-27 22:48:06 +02:00
|
|
|
assert(self.is_stream())
|
2017-10-03 22:33:30 +02:00
|
|
|
assert(self._stream_name is not None)
|
2017-04-27 22:48:06 +02:00
|
|
|
return self._stream_name
|
|
|
|
|
2018-05-10 19:13:36 +02:00
|
|
|
def topic(self) -> str:
|
2017-04-27 22:48:06 +02:00
|
|
|
assert(self.is_stream())
|
2017-10-03 22:33:30 +02:00
|
|
|
assert(self._topic is not None)
|
2017-04-27 22:48:06 +02:00
|
|
|
return self._topic
|
|
|
|
|
|
|
|
@staticmethod
|
2017-11-05 11:15:10 +01:00
|
|
|
def legacy_build(sender: UserProfile,
|
2018-05-10 19:13:36 +02:00
|
|
|
message_type_name: str,
|
|
|
|
message_to: Sequence[str],
|
|
|
|
topic_name: str,
|
2017-11-05 11:15:10 +01:00
|
|
|
realm: Optional[Realm]=None) -> 'Addressee':
|
2017-04-27 22:48:06 +02:00
|
|
|
|
|
|
|
# For legacy reason message_to used to be either a list of
|
|
|
|
# emails or a list of streams. We haven't fixed all of our
|
|
|
|
# callers yet.
|
2017-09-25 21:55:02 +02:00
|
|
|
if realm is None:
|
|
|
|
realm = sender.realm
|
|
|
|
|
2017-04-27 22:48:06 +02:00
|
|
|
if message_type_name == 'stream':
|
|
|
|
if len(message_to) > 1:
|
|
|
|
raise JsonableError(_("Cannot send to multiple streams"))
|
|
|
|
|
|
|
|
if message_to:
|
|
|
|
stream_name = message_to[0]
|
|
|
|
else:
|
|
|
|
# This is a hack to deal with the fact that we still support
|
|
|
|
# default streams (and the None will be converted later in the
|
|
|
|
# callpath).
|
2017-09-28 21:14:08 +02:00
|
|
|
if sender.default_sending_stream:
|
|
|
|
# Use the users default stream
|
|
|
|
stream_name = sender.default_sending_stream.name
|
|
|
|
else:
|
|
|
|
raise JsonableError(_('Missing stream'))
|
2017-04-27 22:48:06 +02:00
|
|
|
|
|
|
|
return Addressee.for_stream(stream_name, topic_name)
|
|
|
|
elif message_type_name == 'private':
|
|
|
|
emails = message_to
|
2017-09-25 21:55:02 +02:00
|
|
|
return Addressee.for_private(emails, realm)
|
2017-04-27 22:48:06 +02:00
|
|
|
else:
|
|
|
|
raise JsonableError(_("Invalid message type"))
|
|
|
|
|
|
|
|
@staticmethod
|
2018-05-10 19:13:36 +02:00
|
|
|
def for_stream(stream_name: str, topic: str) -> 'Addressee':
|
2018-01-20 20:42:40 +01:00
|
|
|
if topic is None:
|
|
|
|
raise JsonableError(_("Missing topic"))
|
|
|
|
topic = topic.strip()
|
|
|
|
if topic == "":
|
|
|
|
raise JsonableError(_("Topic can't be empty"))
|
2017-04-27 22:48:06 +02:00
|
|
|
return Addressee(
|
|
|
|
msg_type='stream',
|
|
|
|
stream_name=stream_name,
|
|
|
|
topic=topic,
|
|
|
|
)
|
|
|
|
|
|
|
|
@staticmethod
|
2018-05-10 19:13:36 +02:00
|
|
|
def for_private(emails: Sequence[str], realm: Realm) -> 'Addressee':
|
2017-09-25 21:52:55 +02:00
|
|
|
user_profiles = get_user_profiles(emails, realm)
|
2017-04-27 22:48:06 +02:00
|
|
|
return Addressee(
|
|
|
|
msg_type='private',
|
2017-08-18 05:01:22 +02:00
|
|
|
user_profiles=user_profiles,
|
2017-04-27 22:48:06 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
@staticmethod
|
2017-11-05 11:15:10 +01:00
|
|
|
def for_user_profile(user_profile: UserProfile) -> 'Addressee':
|
2017-08-18 05:02:02 +02:00
|
|
|
user_profiles = [user_profile]
|
2017-04-27 22:48:06 +02:00
|
|
|
return Addressee(
|
|
|
|
msg_type='private',
|
2017-08-18 05:01:22 +02:00
|
|
|
user_profiles=user_profiles,
|
2017-04-27 22:48:06 +02:00
|
|
|
)
|