2017-07-07 20:35:31 +02:00
|
|
|
# Library code for use in management commands
|
|
|
|
|
2017-08-24 21:46:13 +02:00
|
|
|
import sys
|
2018-08-15 21:02:56 +02:00
|
|
|
import time
|
2017-08-24 21:46:13 +02:00
|
|
|
|
2019-01-09 19:39:29 +01:00
|
|
|
from argparse import ArgumentParser, RawTextHelpFormatter
|
|
|
|
|
2018-05-04 01:04:12 +02:00
|
|
|
from django.conf import settings
|
2017-07-07 20:35:31 +02:00
|
|
|
from django.core.exceptions import MultipleObjectsReturned
|
|
|
|
from django.core.management.base import BaseCommand, CommandError
|
2018-05-11 01:40:23 +02:00
|
|
|
from typing import Any, Dict, Optional, List
|
2017-07-07 20:35:31 +02:00
|
|
|
|
2018-03-14 00:25:31 +01:00
|
|
|
from zerver.models import Realm, UserProfile, Client, get_client
|
2017-07-07 20:35:31 +02:00
|
|
|
|
2017-11-05 11:15:10 +01:00
|
|
|
def is_integer_string(val: str) -> bool:
|
2017-07-07 20:35:31 +02:00
|
|
|
try:
|
|
|
|
int(val)
|
|
|
|
return True
|
|
|
|
except ValueError:
|
|
|
|
return False
|
|
|
|
|
2018-05-04 01:04:12 +02:00
|
|
|
def check_config() -> None:
|
|
|
|
for (setting_name, default) in settings.REQUIRED_SETTINGS:
|
2018-05-14 17:45:32 +02:00
|
|
|
# if required setting is the same as default OR is not found in settings,
|
|
|
|
# throw error to add/set that setting in config
|
2018-05-04 01:04:12 +02:00
|
|
|
try:
|
|
|
|
if settings.__getattr__(setting_name) != default:
|
|
|
|
continue
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
raise CommandError("Error: You must set %s in /etc/zulip/settings.py." % (setting_name,))
|
|
|
|
|
2018-08-15 21:02:56 +02:00
|
|
|
def sleep_forever() -> None:
|
|
|
|
while True: # nocoverage
|
|
|
|
time.sleep(10**9)
|
|
|
|
|
2017-07-07 20:35:31 +02:00
|
|
|
class ZulipBaseCommand(BaseCommand):
|
2019-01-09 19:39:29 +01:00
|
|
|
|
|
|
|
# Fix support for multi-line usage
|
|
|
|
def create_parser(self, *args: Any, **kwargs: Any) -> ArgumentParser:
|
|
|
|
parser = super().create_parser(*args, **kwargs)
|
|
|
|
parser.formatter_class = RawTextHelpFormatter
|
|
|
|
return parser
|
|
|
|
|
2017-11-05 11:15:10 +01:00
|
|
|
def add_realm_args(self, parser: ArgumentParser, required: bool=False,
|
|
|
|
help: Optional[str]=None) -> None:
|
2017-08-07 17:03:33 +02:00
|
|
|
if help is None:
|
|
|
|
help = """The numeric or string ID (subdomain) of the Zulip organization to modify.
|
|
|
|
You can use the command list_realms to find ID of the realms in this server."""
|
|
|
|
|
2017-07-07 20:35:31 +02:00
|
|
|
parser.add_argument(
|
|
|
|
'-r', '--realm',
|
|
|
|
dest='realm_id',
|
|
|
|
required=required,
|
|
|
|
type=str,
|
2017-08-07 17:03:33 +02:00
|
|
|
help=help)
|
2017-07-07 20:35:31 +02:00
|
|
|
|
2017-11-05 11:15:10 +01:00
|
|
|
def add_user_list_args(self, parser: ArgumentParser,
|
2018-05-14 19:17:03 +02:00
|
|
|
help: str='A comma-separated list of email addresses.',
|
|
|
|
all_users_help: str="All users in realm.") -> None:
|
2017-08-19 21:00:19 +02:00
|
|
|
parser.add_argument(
|
|
|
|
'-u', '--users',
|
|
|
|
dest='users',
|
|
|
|
type=str,
|
|
|
|
help=help)
|
|
|
|
|
2018-05-14 19:17:03 +02:00
|
|
|
parser.add_argument(
|
|
|
|
'-a', '--all-users',
|
|
|
|
dest='all_users',
|
|
|
|
action="store_true",
|
|
|
|
default=False,
|
|
|
|
help=all_users_help)
|
2017-08-24 21:46:13 +02:00
|
|
|
|
2017-11-05 11:15:10 +01:00
|
|
|
def get_realm(self, options: Dict[str, Any]) -> Optional[Realm]:
|
2017-07-07 20:35:31 +02:00
|
|
|
val = options["realm_id"]
|
|
|
|
if val is None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
# If they specified a realm argument, we need to ensure the
|
|
|
|
# realm exists. We allow two formats: the numeric ID for the
|
|
|
|
# realm and the string ID of the realm.
|
|
|
|
try:
|
|
|
|
if is_integer_string(val):
|
|
|
|
return Realm.objects.get(id=val)
|
2017-07-07 23:39:55 +02:00
|
|
|
return Realm.objects.get(string_id=val)
|
2017-07-07 20:35:31 +02:00
|
|
|
except Realm.DoesNotExist:
|
2017-08-07 21:32:10 +02:00
|
|
|
raise CommandError("There is no realm with id '%s'. Aborting." %
|
2017-07-07 20:35:31 +02:00
|
|
|
(options["realm_id"],))
|
|
|
|
|
2019-01-11 11:25:36 +01:00
|
|
|
def get_users(self, options: Dict[str, Any], realm: Optional[Realm],
|
|
|
|
is_bot: Optional[bool]=None) -> List[UserProfile]:
|
2017-08-24 21:46:13 +02:00
|
|
|
if "all_users" in options:
|
|
|
|
all_users = options["all_users"]
|
|
|
|
|
2017-08-25 00:10:47 +02:00
|
|
|
if not options["users"] and not all_users:
|
|
|
|
raise CommandError("You have to pass either -u/--users or -a/--all-users.")
|
|
|
|
|
|
|
|
if options["users"] and all_users:
|
2017-08-24 21:46:13 +02:00
|
|
|
raise CommandError("You can't use both -u/--users and -a/--all-users.")
|
|
|
|
|
|
|
|
if all_users and realm is None:
|
|
|
|
raise CommandError("The --all-users option requires a realm; please pass --realm.")
|
|
|
|
|
|
|
|
if all_users:
|
2019-01-11 11:25:36 +01:00
|
|
|
user_profiles = UserProfile.objects.filter(realm=realm)
|
|
|
|
if is_bot is not None:
|
|
|
|
return user_profiles.filter(is_bot=is_bot)
|
|
|
|
return user_profiles
|
2017-08-24 21:46:13 +02:00
|
|
|
|
2017-08-19 21:00:19 +02:00
|
|
|
if options["users"] is None:
|
|
|
|
return []
|
|
|
|
emails = set([email.strip() for email in options["users"].split(",")])
|
|
|
|
user_profiles = []
|
|
|
|
for email in emails:
|
|
|
|
user_profiles.append(self.get_user(email, realm))
|
|
|
|
return user_profiles
|
|
|
|
|
2018-05-11 01:40:23 +02:00
|
|
|
def get_user(self, email: str, realm: Optional[Realm]) -> UserProfile:
|
2017-07-07 20:35:31 +02:00
|
|
|
|
|
|
|
# If a realm is specified, try to find the user there, and
|
|
|
|
# throw an error if they don't exist.
|
|
|
|
if realm is not None:
|
|
|
|
try:
|
2018-12-07 00:05:57 +01:00
|
|
|
return UserProfile.objects.select_related().get(
|
|
|
|
delivery_email__iexact=email.strip(), realm=realm)
|
2017-07-07 20:35:31 +02:00
|
|
|
except UserProfile.DoesNotExist:
|
|
|
|
raise CommandError("The realm '%s' does not contain a user with email '%s'" % (realm, email))
|
|
|
|
|
|
|
|
# Realm is None in the remaining code path. Here, we
|
|
|
|
# optimistically try to see if there is exactly one user with
|
|
|
|
# that email; if so, we'll return it.
|
|
|
|
try:
|
2018-12-07 00:05:57 +01:00
|
|
|
return UserProfile.objects.select_related().get(delivery_email__iexact=email.strip())
|
2017-07-07 20:35:31 +02:00
|
|
|
except MultipleObjectsReturned:
|
|
|
|
raise CommandError("This Zulip server contains multiple users with that email " +
|
2017-11-10 03:34:13 +01:00
|
|
|
"(in different realms); please pass `--realm` "
|
|
|
|
"to specify which one to modify.")
|
2017-07-07 20:35:31 +02:00
|
|
|
except UserProfile.DoesNotExist:
|
|
|
|
raise CommandError("This Zulip server does not contain a user with email '%s'" % (email,))
|
2018-03-14 00:25:31 +01:00
|
|
|
|
|
|
|
def get_client(self) -> Client:
|
|
|
|
"""Returns a Zulip Client object to be used for things done in management commands"""
|
|
|
|
return get_client("ZulipServer")
|