mirror of
https://github.com/zulip/zulip.git
synced 2025-11-01 20:44:04 +00:00
This is a preparatory commit for using isort for sorting all of our imports, merging changes to files where we can easily review the changes as something we're happy with. These are also files with relatively little active development, which means we don't expect much merge conflict risk from these changes.
108 lines
4.8 KiB
Python
108 lines
4.8 KiB
Python
import hashlib
|
|
import shutil
|
|
import subprocess
|
|
from argparse import ArgumentParser
|
|
from typing import Any, Dict, List
|
|
|
|
from zerver.lib.management import CommandError, ZulipBaseCommand
|
|
from zerver.lib.send_email import FromAddress, send_email
|
|
from zerver.models import UserProfile
|
|
from zerver.templatetags.app_filters import render_markdown_path
|
|
|
|
|
|
def send_custom_email(users: List[UserProfile], options: Dict[str, Any]) -> None:
|
|
"""
|
|
Can be used directly with from a management shell with
|
|
send_custom_email(user_profile_list, dict(
|
|
markdown_template_path="/path/to/markdown/file.md",
|
|
subject="Email Subject",
|
|
from_name="Sender Name")
|
|
)
|
|
"""
|
|
|
|
with open(options["markdown_template_path"], "r") as f:
|
|
email_template_hash = hashlib.sha256(f.read().encode('utf-8')).hexdigest()[0:32]
|
|
email_id = "zerver/emails/custom_email_%s" % (email_template_hash,)
|
|
markdown_email_base_template_path = "templates/zerver/emails/custom_email_base.pre.html"
|
|
html_source_template_path = "templates/%s.source.html" % (email_id,)
|
|
plain_text_template_path = "templates/%s.txt" % (email_id,)
|
|
subject_path = "templates/%s.subject.txt" % (email_id,)
|
|
|
|
# First, we render the markdown input file just like our
|
|
# user-facing docs with render_markdown_path.
|
|
shutil.copyfile(options['markdown_template_path'], plain_text_template_path)
|
|
rendered_input = render_markdown_path(plain_text_template_path.replace("templates/", ""))
|
|
|
|
# And then extend it with our standard email headers.
|
|
with open(html_source_template_path, "w") as f:
|
|
with open(markdown_email_base_template_path, "r") as base_template:
|
|
# Note that we're doing a hacky non-Jinja2 substitution here;
|
|
# we do this because the normal render_markdown_path ordering
|
|
# doesn't commute properly with inline-email-css.
|
|
f.write(base_template.read().replace('{{ rendered_input }}',
|
|
rendered_input))
|
|
|
|
with open(subject_path, "w") as f:
|
|
f.write(options["subject"])
|
|
|
|
# Then, we compile the email template using inline-email-css to
|
|
# add our standard styling to the paragraph tags (etc.).
|
|
#
|
|
# TODO: Ideally, we'd just refactor inline-email-css to
|
|
# compile this one template, not all of them.
|
|
subprocess.check_call(["./scripts/setup/inline-email-css"])
|
|
|
|
# Finally, we send the actual emails.
|
|
for user_profile in users:
|
|
context = {
|
|
'realm_uri': user_profile.realm.uri,
|
|
'realm_name': user_profile.realm.name,
|
|
}
|
|
send_email(email_id, to_user_ids=[user_profile.id],
|
|
from_address=FromAddress.SUPPORT,
|
|
reply_to_email=options.get("reply_to"),
|
|
from_name=options["from_name"], context=context)
|
|
|
|
class Command(ZulipBaseCommand):
|
|
help = """Send email to specified email address."""
|
|
|
|
def add_arguments(self, parser: ArgumentParser) -> None:
|
|
parser.add_argument('--entire-server', action="store_true", default=False,
|
|
help="Send to every user on the server. ")
|
|
parser.add_argument('--markdown-template-path', '--path',
|
|
dest='markdown_template_path',
|
|
required=True,
|
|
type=str,
|
|
help='Path to a markdown-format body for the email')
|
|
parser.add_argument('--subject',
|
|
required=True,
|
|
type=str,
|
|
help='Subject line for the email')
|
|
parser.add_argument('--from-name',
|
|
required=True,
|
|
type=str,
|
|
help='From line for the email')
|
|
parser.add_argument('--reply-to',
|
|
type=str,
|
|
help='Optional reply-to line for the email')
|
|
|
|
self.add_user_list_args(parser,
|
|
help="Email addresses of user(s) to send emails to.",
|
|
all_users_help="Send to every user on the realm.")
|
|
self.add_realm_args(parser)
|
|
|
|
def handle(self, *args: Any, **options: str) -> None:
|
|
if options["entire_server"]:
|
|
users = UserProfile.objects.filter(is_active=True, is_bot=False,
|
|
is_mirror_dummy=False)
|
|
else:
|
|
realm = self.get_realm(options)
|
|
try:
|
|
users = self.get_users(options, realm, is_bot=False)
|
|
except CommandError as error:
|
|
if str(error) == "You have to pass either -u/--users or -a/--all-users.":
|
|
raise CommandError("You have to pass -u/--users or -a/--all-users or --entire-server.")
|
|
raise error
|
|
|
|
send_custom_email(users, options)
|