From e3e4694e339605f8054de0cdd856f54f4a3f173f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Such=C3=A1nek?= Date: Fri, 21 Jul 2023 11:02:14 +0200 Subject: [PATCH] [DSW-2011] Improve mailer versatility --- .../dsw-database/dsw/database/database.py | 24 +- packages/dsw-mailer/Dockerfile | 1 - packages/dsw-mailer/dsw/mailer/cli.py | 7 +- packages/dsw-mailer/dsw/mailer/consts.py | 1 + packages/dsw-mailer/dsw/mailer/context.py | 20 +- packages/dsw-mailer/dsw/mailer/mailer.py | 578 ++---------------- packages/dsw-mailer/dsw/mailer/model.py | 15 +- packages/dsw-mailer/dsw/mailer/templates.py | 30 +- ...msg.registry.registrationConfirmation.json | 2 +- ...registry.registrationCreatedAnalytics.json | 2 +- .../examples/msg.registry.resetToken.json | 2 +- .../msg.wizard.questionnaireInvitation.json | 2 +- .../msg.wizard.registrationConfirmation.json | 2 +- ...g.wizard.registrationCreatedAnalytics.json | 2 +- .../examples/msg.wizard.resetPassword.json | 2 +- .../templates/_common/footer.html.j2 | 2 + .../templates/_common/footer.txt.j2 | 2 +- .../registrationConfirmation/message.html.j2 | 12 +- .../registrationConfirmation/message.json | 7 +- .../registrationConfirmation/message.txt.j2 | 8 +- .../message.html.j2 | 10 +- .../registrationCreatedAnalytics/message.json | 7 +- .../message.txt.j2 | 7 +- .../registry/resetToken/message.html.j2 | 6 +- .../registry/resetToken/message.json | 7 +- .../registry/resetToken/message.txt.j2 | 2 +- .../questionnaireInvitation/message.html.j2 | 8 +- .../questionnaireInvitation/message.json | 7 +- .../questionnaireInvitation/message.txt.j2 | 6 +- .../registrationConfirmation/message.html.j2 | 6 +- .../registrationConfirmation/message.json | 7 +- .../registrationConfirmation/message.txt.j2 | 4 +- .../message.html.j2 | 6 +- .../registrationCreatedAnalytics/message.json | 7 +- .../message.txt.j2 | 6 +- .../wizard/resetPassword/message.html.j2 | 6 +- .../wizard/resetPassword/message.json | 7 +- .../wizard/resetPassword/message.txt.j2 | 4 +- .../wizard/twoFactorAuth/message.html.j2 | 6 +- .../wizard/twoFactorAuth/message.json | 7 +- .../wizard/twoFactorAuth/message.txt.j2 | 2 +- 41 files changed, 196 insertions(+), 653 deletions(-) diff --git a/packages/dsw-database/dsw/database/database.py b/packages/dsw-database/dsw/database/database.py index e0dc912d..cfc8f5ca 100644 --- a/packages/dsw-database/dsw/database/database.py +++ b/packages/dsw-database/dsw/database/database.py @@ -49,6 +49,8 @@ class Database: 'WHERE document_template_id = %s AND app_uuid = %s;' SELECT_TEMPLATE_ASSETS = 'SELECT * FROM document_template_asset ' \ 'WHERE document_template_id = %s AND app_uuid = %s;' + CHECK_TABLE_EXISTS = 'SELECT EXISTS(SELECT * FROM information_schema.tables' \ + ' WHERE table_name = %(table_name)s)' SELECT_MAIL_CONFIG = 'SELECT icm.* ' \ 'FROM app_config ac JOIN instance_config_mail icm ' \ 'ON ac.mail_config_uuid = icm.uuid ' \ @@ -89,6 +91,24 @@ def connect(self): self.conn_query.connect() self.conn_queue.connect() + @tenacity.retry( + reraise=True, + wait=tenacity.wait_exponential(multiplier=RETRY_QUERY_MULTIPLIER), + stop=tenacity.stop_after_attempt(RETRY_QUERY_TRIES), + before=tenacity.before_log(LOG, logging.DEBUG), + after=tenacity.after_log(LOG, logging.DEBUG), + ) + def _check_table_exists(self, table_name: str) -> bool: + with self.conn_query.new_cursor(use_dict=True) as cursor: + try: + cursor.execute( + query=self.CHECK_TABLE_EXISTS, + params={'table_name': table_name}, + ) + return bool(cursor.rowcount) + except Exception: + return False + @tenacity.retry( reraise=True, wait=tenacity.wait_exponential(multiplier=RETRY_QUERY_MULTIPLIER), @@ -356,6 +376,8 @@ def get_app_config(self, app_uuid: str) -> Optional[DBAppConfig]: ) def get_mail_config(self, app_uuid: str) -> Optional[DBInstanceConfigMail]: with self.conn_query.new_cursor(use_dict=True) as cursor: + if not self._check_table_exists(table_name='instance_config_mail'): + return None try: cursor.execute( query=self.SELECT_MAIL_CONFIG, @@ -366,7 +388,7 @@ def get_mail_config(self, app_uuid: str) -> Optional[DBInstanceConfigMail]: return None return DBInstanceConfigMail.from_dict_row(data=result) except Exception as e: - LOG.warning(f'Could not retrieve instance_mail_config for app' + LOG.warning(f'Could not retrieve instance_config_mail for app' f' "{app_uuid}": {str(e)}') return None diff --git a/packages/dsw-mailer/Dockerfile b/packages/dsw-mailer/Dockerfile index b43ec86a..17463602 100644 --- a/packages/dsw-mailer/Dockerfile +++ b/packages/dsw-mailer/Dockerfile @@ -14,7 +14,6 @@ FROM datastewardshipwizard/python-base:3.11-alpine ENV APPLICATION_CONFIG_PATH /app/config/application.yml ENV WORKDIR_PATH /home/user/templates -ENV MAILER_MODE wizard ENV PATH "/home/user/.local/bin:$PATH" # Setup non-root user diff --git a/packages/dsw-mailer/dsw/mailer/cli.py b/packages/dsw-mailer/dsw/mailer/cli.py index 6062e119..b1ea537c 100644 --- a/packages/dsw-mailer/dsw/mailer/cli.py +++ b/packages/dsw-mailer/dsw/mailer/cli.py @@ -51,14 +51,11 @@ def extract_message_request(ctx, param, value: IO): type=click.File('r', encoding='utf-8')) @click.option('-w', '--workdir', envvar='WORKDIR_PATH', type=click.Path(dir_okay=True, exists=True)) -@click.option('-m', '--mode', envvar='MAILER_MODE', - type=click.Choice(['wizard', 'registry']), - default='wizard') -def cli(ctx, config: MailerConfig, workdir: str, mode: str): +def cli(ctx, config: MailerConfig, workdir: str): path_workdir = pathlib.Path(workdir) from .mailer import Mailer config.log.apply() - ctx.obj['mailer'] = Mailer(config, path_workdir, mode) + ctx.obj['mailer'] = Mailer(config, path_workdir) @cli.command(name='send', help='Send message(s) from given file directly.') diff --git a/packages/dsw-mailer/dsw/mailer/consts.py b/packages/dsw-mailer/dsw/mailer/consts.py index 2c5e75dd..d0145ac5 100644 --- a/packages/dsw-mailer/dsw/mailer/consts.py +++ b/packages/dsw-mailer/dsw/mailer/consts.py @@ -1,6 +1,7 @@ COMPONENT_NAME = 'Mailer' CMD_CHANNEL = 'mailer' CMD_COMPONENT = 'mailer' +CMD_FUNCTION = 'sendMail' DEFAULT_ENCODING = 'utf-8' NULL_UUID = '00000000-0000-0000-0000-000000000000' PROG_NAME = 'dsw-mailer' diff --git a/packages/dsw-mailer/dsw/mailer/context.py b/packages/dsw-mailer/dsw/mailer/context.py index caaa0578..2c3fcad4 100644 --- a/packages/dsw-mailer/dsw/mailer/context.py +++ b/packages/dsw-mailer/dsw/mailer/context.py @@ -34,11 +34,10 @@ def __init__(self, trace_id: str): class _Context: def __init__(self, app: AppContext, job: JobContext, - templates: TemplateRegistry, mode: str): + templates: TemplateRegistry): self.app = app self.job = job self.templates = templates - self.mode = mode def update_trace_id(self, trace_id: str): self.app.cfg.log.set_logging_extra('traceId', trace_id) @@ -59,15 +58,7 @@ def get(cls) -> _Context: return cls._instance @classmethod - def is_registry_mode(cls): - return cls.get().mode == 'registry' - - @classmethod - def is_wizard_mode(cls): - return cls.get().mode == 'wizard' - - @classmethod - def initialize(cls, db, config, sender, workdir, mode): + def initialize(cls, db, config, sender, workdir): cls._instance = _Context( app=AppContext( db=db, @@ -81,12 +72,5 @@ def initialize(cls, db, config, sender, workdir, mode): templates=TemplateRegistry( cfg=config, workdir=workdir, - mode=mode, ), - mode=mode, ) - if cls.get().app.cfg.mail.name == '': - if cls.is_registry_mode(): - cls.get().app.cfg.mail.name = 'DSW Registry' - elif cls.is_wizard_mode(): - cls.get().app.cfg.mail.name = 'DSW' diff --git a/packages/dsw-mailer/dsw/mailer/mailer.py b/packages/dsw-mailer/dsw/mailer/mailer.py index be5b1bcd..10b8929d 100644 --- a/packages/dsw-mailer/dsw/mailer/mailer.py +++ b/packages/dsw-mailer/dsw/mailer/mailer.py @@ -1,4 +1,3 @@ -import abc import datetime import dateutil.parser import logging @@ -12,14 +11,14 @@ from dsw.command_queue import CommandWorker, CommandQueue from dsw.config.sentry import SentryReporter from dsw.database.database import Database -from dsw.database.model import DBAppConfig, PersistentCommand, \ - DBInstanceConfigMail +from dsw.database.model import PersistentCommand, DBInstanceConfigMail from .build_info import BUILD_INFO from .config import MailerConfig, MailConfig from .consts import PROG_NAME from .smtp import SMTPSender -from .consts import COMPONENT_NAME, CMD_CHANNEL, CMD_COMPONENT +from .consts import COMPONENT_NAME, CMD_CHANNEL, CMD_COMPONENT, \ + CMD_FUNCTION from .context import Context from .model import MessageRequest @@ -29,7 +28,7 @@ class Mailer(CommandWorker): - def __init__(self, cfg: MailerConfig, workdir: pathlib.Path, mode: str): + def __init__(self, cfg: MailerConfig, workdir: pathlib.Path): self.cfg = cfg self.workdir = workdir self.rate_limiter = RateLimiter( @@ -37,16 +36,15 @@ def __init__(self, cfg: MailerConfig, workdir: pathlib.Path, mode: str): count=cfg.mail.rate_limit_count, ) - self._init_context(workdir=workdir, mode=mode) + self._init_context(workdir=workdir) self.ctx = Context.get() - def _init_context(self, workdir: pathlib.Path, mode: str): + def _init_context(self, workdir: pathlib.Path): Context.initialize( config=self.cfg, workdir=workdir, db=Database(cfg=self.cfg.db, connect=False), sender=SMTPSender(cfg=self.cfg.mail), - mode=mode, ) SentryReporter.initialize( dsn=self.cfg.sentry.workers_dsn, @@ -89,18 +87,15 @@ def work(self, cmd: PersistentCommand): Context.get().update_trace_id(cmd.uuid) # work app_ctx = Context.get().app - mc = load_mailer_command(cmd) - mc.prepare(app_ctx.db) + mc = MailerCommand.load(cmd) rq = mc.to_request( msg_id=cmd.uuid, trigger='PersistentComment', ) # get mailer config from DB - cfg = None - if Context.is_wizard_mode(): - cfg = _transform_mail_config( - cfg=app_ctx.db.get_mail_config(app_uuid=cmd.app_uuid), - ) + cfg = _transform_mail_config( + cfg=app_ctx.db.get_mail_config(app_uuid=cmd.app_uuid), + ) LOG.debug(f'Config from DB: {cfg}') # client URL rq.client_url = cmd.body.get('clientUrl', app_ctx.cfg.general.client_url) @@ -152,11 +147,6 @@ def _transform_mail_config(cfg: Optional[DBInstanceConfigMail]) -> Optional[Mail ) -######################################################################################### -# Commands and their logic -######################################################################################### - - class RateLimiter: def __init__(self, window: int, count: int): @@ -182,528 +172,50 @@ def hit(self): time.sleep(sleep_time) -def _app_config_to_context(app_config: Optional[DBAppConfig]) -> dict: - if app_config is None: - return {} - return { - 'supportEmail': app_config.support_email, - 'appTitle': app_config.app_title, - } - - -class MailerCommand(abc.ABC): - - FUNCTION_NAME = 'unknown' - TEMPLATE_NAME = '' - - @abc.abstractmethod - def to_context(self) -> dict: - pass - - @abc.abstractmethod - def to_request(self, msg_id: str, trigger: str) -> MessageRequest: - pass - - def prepare(self, db: Database): - pass - - @classmethod - def corresponds(cls, cmd: PersistentCommand) -> bool: - return cls.FUNCTION_NAME == cmd.function - - @staticmethod - @abc.abstractmethod - def create_from(cmd: PersistentCommand) -> 'MailerCommand': - pass - - -class CmdUser: - - def __init__(self, user_uuid: str, first_name: str, last_name: str, - email: str): - self.uuid = user_uuid - self.first_name = first_name - self.last_name = last_name - self.email = email - - def to_context(self) -> dict: - return { - 'uuid': self.uuid, - 'firstName': self.first_name, - 'lastName': self.last_name, - 'email': self.email - } - +class MailerCommand: -class CmdOrg: - - def __init__(self, org_id: str, name: str, email: str): - self.id = org_id - self.name = name - self.email = email - - def to_context(self) -> dict: - return { - 'id': self.id, - 'name': self.name, - 'email': self.email - } - - -class MailerWizardCommand(MailerCommand): - - def __init__(self, app_uuid: str): + def __init__(self, recipients: list[str], mode: str, template: str, ctx: dict, + app_uuid: str, cmd_uuid: str): + self.mode = mode + self.template = template + self.recipients = recipients + self.ctx = ctx self.app_uuid = app_uuid - self.app_config = None # type: Optional[DBAppConfig] - - def prepare(self, db: Database): - self.app_config = db.get_app_config( - app_uuid=self.app_uuid, - ) - - -class _MWRegistrationConfirmation(MailerWizardCommand): - - FUNCTION_NAME = 'sendRegistrationConfirmationMail' - TEMPLATE_NAME = 'registrationConfirmation' - - def __init__(self, email: str, user: CmdUser, code: str, - client_url: str, app_uuid: str): - super().__init__(app_uuid=app_uuid) - self.email = email - self.user = user - self.code = code - self.client_url = client_url - - @property - def activation_link(self): - return f'{self.client_url}/signup/{self.user.uuid}/{self.code}' - - def to_context(self) -> dict: - ctx = { - 'user': self.user.to_context(), - 'activationLink': self.activation_link, - 'clientUrl': self.client_url, - 'hash': self.code, - } - ctx.update(_app_config_to_context(self.app_config)) - return ctx - - def to_request(self, msg_id: str, trigger: str) -> MessageRequest: - return MessageRequest( - message_id=msg_id, - template_name=self.TEMPLATE_NAME, - trigger=trigger, - ctx=self.to_context(), - recipients=[self.email], - ) - - @classmethod - def corresponds(cls, cmd: PersistentCommand) -> bool: - return cls.FUNCTION_NAME == cmd.function and 'userUuid' in cmd.body - - @staticmethod - def create_from(cmd: PersistentCommand) -> MailerCommand: - return _MWRegistrationConfirmation( - email=cmd.body['email'], - user=CmdUser( - user_uuid=cmd.body['userUuid'], - first_name=cmd.body['userFirstName'], - last_name=cmd.body['userLastName'], - email=cmd.body['userEmail'], - ), - code=cmd.body['hash'], - client_url=cmd.body['clientUrl'], - app_uuid=cmd.app_uuid, - ) - - -class _MWQuestionnaireInvitation(MailerWizardCommand): - - FUNCTION_NAME = 'sendQuestionnaireInvitationMail' - TEMPLATE_NAME = 'questionnaireInvitation' - - def __init__(self, email: str, invitee: CmdUser, owner: CmdUser, - project_uuid: str, project_name: str, - client_url: str, app_uuid: str): - super().__init__(app_uuid=app_uuid) - self.email = email - self.invitee = invitee - self.owner = owner - self.project_uuid = project_uuid - self.project_name = project_name - self.client_url = client_url - - @property - def project_link(self): - return f'{self.client_url}/projects/{self.project_uuid}' - - def to_context(self) -> dict: - ctx = { - 'invitee': self.invitee.to_context(), - 'owner': self.owner.to_context(), - 'project': { - 'uuid': self.project_uuid, - 'name': self.project_name, - 'link': self.project_link, - }, - 'clientUrl': self.client_url, - } - ctx.update(_app_config_to_context(self.app_config)) - return ctx - - def to_request(self, msg_id: str, trigger: str) -> MessageRequest: - return MessageRequest( - message_id=msg_id, - template_name=self.TEMPLATE_NAME, - trigger=trigger, - ctx=self.to_context(), - recipients=[self.email], - ) - - @staticmethod - def create_from(cmd: PersistentCommand) -> MailerCommand: - return _MWQuestionnaireInvitation( - email=cmd.body['email'], - invitee=CmdUser( - user_uuid=cmd.body['inviteeUuid'], - first_name=cmd.body['inviteeFirstName'], - last_name=cmd.body['inviteeLastName'], - email=cmd.body['inviteeEmail'], - ), - owner=CmdUser( - user_uuid=cmd.body['ownerUuid'], - first_name=cmd.body['ownerFirstName'], - last_name=cmd.body['ownerLastName'], - email=cmd.body['ownerEmail'], - ), - project_uuid=cmd.body['questionnaireUuid'], - project_name=cmd.body['questionnaireName'], - client_url=cmd.body['clientUrl'], - app_uuid=cmd.app_uuid, - ) - - -class _MWRegistrationCreatedAnalytics(MailerWizardCommand): - - FUNCTION_NAME = 'sendRegistrationCreatedAnalyticsMail' - TEMPLATE_NAME = 'registrationCreatedAnalytics' - - def __init__(self, email: str, user: CmdUser, client_url: str, app_uuid: str): - super().__init__(app_uuid=app_uuid) - self.email = email - self.user = user - self.client_url = client_url - - def to_context(self) -> dict: - ctx = { - 'user': self.user.to_context(), - 'clientUrl': self.client_url, - } - ctx.update(_app_config_to_context(self.app_config)) - return ctx - - def to_request(self, msg_id: str, trigger: str) -> MessageRequest: - return MessageRequest( - message_id=msg_id, - template_name=self.TEMPLATE_NAME, - trigger=trigger, - ctx=self.to_context(), - recipients=[self.email], - ) - - @classmethod - def corresponds(cls, cmd: PersistentCommand) -> bool: - return cls.FUNCTION_NAME == cmd.function and 'userUuid' in cmd.body - - @staticmethod - def create_from(cmd: PersistentCommand) -> MailerCommand: - return _MWRegistrationCreatedAnalytics( - email=cmd.body['email'], - user=CmdUser( - user_uuid=cmd.body['userUuid'], - first_name=cmd.body['userFirstName'], - last_name=cmd.body['userLastName'], - email=cmd.body['userEmail'], - ), - client_url=cmd.body['clientUrl'], - app_uuid=cmd.app_uuid, - ) - - -class _MWResetPassword(MailerWizardCommand): - - FUNCTION_NAME = 'sendResetPasswordMail' - TEMPLATE_NAME = 'resetPassword' - - def __init__(self, email: str, user: CmdUser, code: str, - client_url: str, app_uuid: str): - super().__init__(app_uuid=app_uuid) - self.email = email - self.user = user - self.code = code - self.client_url = client_url - - @property - def reset_link(self): - return f'{self.client_url}/forgotten-password/{self.user.uuid}/{self.code}' - - def to_context(self) -> dict: - ctx = { - 'user': self.user.to_context(), - 'resetLink': self.reset_link, - 'clientUrl': self.client_url, - 'hash': self.code, - } - ctx.update(_app_config_to_context(self.app_config)) - return ctx + self.cmd_uuid = cmd_uuid + self._enrich_context() def to_request(self, msg_id: str, trigger: str) -> MessageRequest: return MessageRequest( message_id=msg_id, - template_name=self.TEMPLATE_NAME, + template_name=f'{self.mode}:{self.template}', trigger=trigger, - ctx=self.to_context(), - recipients=[self.email], - ) - - @staticmethod - def create_from(cmd: PersistentCommand) -> MailerCommand: - return _MWResetPassword( - email=cmd.body['email'], - user=CmdUser( - user_uuid=cmd.body['userUuid'], - first_name=cmd.body['userFirstName'], - last_name=cmd.body['userLastName'], - email=cmd.body['userEmail'], - ), - code=cmd.body['hash'], - client_url=cmd.body['clientUrl'], - app_uuid=cmd.app_uuid, + ctx=self.ctx, + recipients=self.recipients, ) - -class _MWTwoFactorAuth(MailerWizardCommand): - - FUNCTION_NAME = 'sendTwoFactorAuthMail' - TEMPLATE_NAME = 'twoFactorAuth' - - def __init__(self, email: str, user: CmdUser, code: str, - client_url: str, app_uuid: str): - super().__init__(app_uuid=app_uuid) - self.email = email - self.user = user - self.code = code - self.client_url = client_url - - def to_context(self) -> dict: - ctx = { - 'user': self.user.to_context(), - 'code': self.code, - 'clientUrl': self.client_url, + def _enrich_context(self): + self.ctx['_meta'] = { + 'cmd_uuid': self.cmd_uuid, + 'recipients': self.recipients, + 'mode': self.mode, + 'template': self.template, + 'now': datetime.datetime.now(), } - ctx.update(_app_config_to_context(self.app_config)) - return ctx - - def to_request(self, msg_id: str, trigger: str) -> MessageRequest: - return MessageRequest( - message_id=msg_id, - template_name=self.TEMPLATE_NAME, - trigger=trigger, - ctx=self.to_context(), - recipients=[self.email], - ) - - @staticmethod - def create_from(cmd: PersistentCommand) -> MailerCommand: - return _MWTwoFactorAuth( - email=cmd.body['email'], - user=CmdUser( - user_uuid=cmd.body['userUuid'], - first_name=cmd.body['userFirstName'], - last_name=cmd.body['userLastName'], - email=cmd.body['userEmail'], - ), - code=cmd.body['code'], - client_url=cmd.body['clientUrl'], - app_uuid=cmd.app_uuid, - ) - - -class _MRRegistrationConfirmation(MailerCommand): - - FUNCTION_NAME = 'sendRegistrationConfirmationMail' - TEMPLATE_NAME = 'registrationConfirmation' - - def __init__(self, email: str, org: CmdOrg, code: str, - client_url: str, callback_url: Optional[str]): - self.email = email - self.org = org - self.code = code - self.client_url = client_url - self.callback_url = callback_url - - @property - def registry_link(self) -> str: - return f'{self.client_url}/signup/{self.org.id}/{self.code}' - - @property - def callback_link(self) -> Optional[str]: - if self.callback_url is None: - return None - return f'{self.callback_url}/registry/signup/{self.org.id}/{self.code}' - - @property - def activation_link(self) -> Optional[str]: - if self.callback_url is None: - return self.registry_link - return self.callback_link - - def to_context(self) -> dict: - return { - 'organization': self.org.to_context(), - 'hash': self.code, - 'registryLink': self.registry_link, - 'callbackLink': self.callback_link, - 'activationLink': self.activation_link, - 'clientUrl': self.client_url, - } - - def to_request(self, msg_id: str, trigger: str) -> MessageRequest: - return MessageRequest( - message_id=msg_id, - template_name=self.TEMPLATE_NAME, - trigger=trigger, - ctx=self.to_context(), - recipients=[self.email], - ) - - @classmethod - def corresponds(cls, cmd: PersistentCommand) -> bool: - return cls.FUNCTION_NAME == cmd.function and 'organizationId' in cmd.body @staticmethod - def create_from(cmd: PersistentCommand) -> MailerCommand: - return _MRRegistrationConfirmation( - email=cmd.body['email'], - org=CmdOrg( - org_id=cmd.body['organizationId'], - name=cmd.body['organizationName'], - email=cmd.body['organizationEmail'], - ), - code=cmd.body['hash'], - client_url=cmd.body['clientUrl'], - callback_url=cmd.body.get('callbackUrl', None), - ) - - -class _MRRegistrationCreatedAnalytics(MailerCommand): - - FUNCTION_NAME = 'sendRegistrationCreatedAnalyticsMail' - TEMPLATE_NAME = 'registrationCreatedAnalytics' - - def __init__(self, email: str, org: CmdOrg, client_url: str): - self.email = email - self.org = org - self.client_url = client_url - - def to_context(self) -> dict: - return { - 'organization': self.org.to_context(), - 'clientUrl': self.client_url, - } - - def to_request(self, msg_id: str, trigger: str) -> MessageRequest: - return MessageRequest( - message_id=msg_id, - template_name=self.TEMPLATE_NAME, - trigger=trigger, - ctx=self.to_context(), - recipients=[self.email], - ) - - @classmethod - def corresponds(cls, cmd: PersistentCommand) -> bool: - return cls.FUNCTION_NAME == cmd.function and 'organizationId' in cmd.body - - @staticmethod - def create_from(cmd: PersistentCommand) -> MailerCommand: - return _MRRegistrationCreatedAnalytics( - email=cmd.body['email'], - org=CmdOrg( - org_id=cmd.body['organizationId'], - name=cmd.body['organizationName'], - email=cmd.body['organizationEmail'], - ), - client_url=cmd.body['clientUrl'], - ) - - -class _MRResetToken(MailerCommand): - - FUNCTION_NAME = 'sendResetTokenMail' - TEMPLATE_NAME = 'resetToken' - - def __init__(self, email: str, org: CmdOrg, code: str, client_url: str): - self.email = email - self.org = org - self.code = code - self.client_url = client_url - - @property - def reset_link(self): - return f'{self.client_url}/forgotten-token/{self.org.id}/{self.code}' - - def to_context(self) -> dict: - return { - 'organization': self.org.to_context(), - 'resetLink': self.reset_link, - 'hash': self.code, - 'clientUrl': self.client_url, - } - - def to_request(self, msg_id: str, trigger: str) -> MessageRequest: - return MessageRequest( - message_id=msg_id, - template_name=self.TEMPLATE_NAME, - trigger=trigger, - ctx=self.to_context(), - recipients=[self.email], - ) - - @staticmethod - def create_from(cmd: PersistentCommand) -> MailerCommand: - return _MRResetToken( - email=cmd.body['email'], - org=CmdOrg( - org_id=cmd.body['organizationId'], - name=cmd.body['organizationName'], - email=cmd.body['organizationEmail'], - ), - code=cmd.body['hash'], - client_url=cmd.body['clientUrl'], - ) - - -_MAILER_COMMANDS = [ - _MWRegistrationConfirmation, - _MWResetPassword, - _MWQuestionnaireInvitation, - _MWRegistrationCreatedAnalytics, - _MWTwoFactorAuth, - _MRRegistrationConfirmation, - _MRResetToken, - _MRRegistrationCreatedAnalytics, -] - - -def load_mailer_command(cmd: PersistentCommand) -> MailerCommand: - if cmd.component != CMD_COMPONENT: - raise RuntimeError('Tried to process non-mailer command') - for CMD_TYPE in _MAILER_COMMANDS: - if issubclass(CMD_TYPE, MailerCommand) and CMD_TYPE.corresponds(cmd): - try: - return CMD_TYPE.create_from(cmd) - except KeyError as e: - raise RuntimeError(f'Cannot parse command: {str(e)}') - raise RuntimeError('Cannot process such command') + def load(cmd: PersistentCommand) -> 'MailerCommand': + if cmd.component != CMD_COMPONENT: + raise RuntimeError('Tried to process non-mailer command') + if cmd.function != CMD_FUNCTION: + raise RuntimeError(f'Unsupported function: {cmd.function}') + try: + return MailerCommand( + mode=cmd.body['mode'], + template=cmd.body['template'], + recipients=cmd.body['recipients'], + ctx=cmd.body['parameters'], + app_uuid=cmd.app_uuid, + cmd_uuid=cmd.uuid, + ) + except KeyError as e: + raise RuntimeError(f'Cannot parse command: {str(e)}') diff --git a/packages/dsw-mailer/dsw/mailer/model.py b/packages/dsw-mailer/dsw/mailer/model.py index e7bc3978..0f330155 100644 --- a/packages/dsw-mailer/dsw/mailer/model.py +++ b/packages/dsw-mailer/dsw/mailer/model.py @@ -49,11 +49,14 @@ def load_from_file(data: dict) -> 'TemplateDescriptorPart': class TemplateDescriptor: - def __init__(self, message_id: str, subject: str, language: str, + def __init__(self, message_id: str, subject: str, subject_prefix: bool, + default_sender_name: Optional[str], language: str, importance: str, sensitivity: Optional[str], priority: Optional[str]): self.id = message_id self.subject = subject + self.use_subject_prefix = subject_prefix + self.default_sender_name = default_sender_name self.language = language self.importance = importance self.sensitivity = sensitivity @@ -66,6 +69,8 @@ def load_from_file(data: dict) -> 'TemplateDescriptor': result = TemplateDescriptor( message_id=data.get('id', ''), subject=data.get('subject', ''), + subject_prefix=data.get('subjectPrefix', True), + default_sender_name=data.get('defaultSenderName', None), language=data.get('language', 'en'), importance=data.get('importance', 'normal'), sensitivity=data.get('sensitivity', None), @@ -103,10 +108,10 @@ def load_from_file(data: dict) -> 'MessageRequest': class MailMessage: def __init__(self): - self.from_mail = '' - self.from_name = '' - self.recipients = list() - self.subject = '' + self.from_mail = '' # type: str + self.from_name = None # type: Optional[str] + self.recipients = list() # type: list[str] + self.subject = '' # type: str self.plain_body = None # type: Optional[str] self.html_body = None # type: Optional[str] self.html_images = list() # type: list[MailAttachment] diff --git a/packages/dsw-mailer/dsw/mailer/templates.py b/packages/dsw-mailer/dsw/mailer/templates.py index c3352434..1cd100fa 100644 --- a/packages/dsw-mailer/dsw/mailer/templates.py +++ b/packages/dsw-mailer/dsw/mailer/templates.py @@ -26,24 +26,26 @@ def __init__(self, name: str, descriptor: TemplateDescriptor, self.attachments = list() # type: list[MailAttachment] self.html_images = list() # type: list[MailAttachment] - def render(self, rq: MessageRequest, mail_name: str, mail_from: str) -> MailMessage: + def render(self, rq: MessageRequest, mail_name: Optional[str], mail_from: str) -> MailMessage: ctx = rq.ctx msg = MailMessage() msg.recipients = rq.recipients - subject_prefix = ctx.get('appTitle', None) - if subject_prefix is None: - subject_prefix = mail_name - msg.subject = f'{subject_prefix}: {self.descriptor.subject}' + if self.descriptor.use_subject_prefix: + subject_prefix = ctx.get('appTitle', None) or mail_name + if subject_prefix is None: + subject_prefix = self.descriptor.default_sender_name + ctx['appTitle'] = subject_prefix + msg.subject = f'{subject_prefix}: {self.descriptor.subject}' + else: + msg.subject = self.descriptor.subject msg.msg_id = rq.id msg.msg_domain = rq.domain msg.language = self.descriptor.language msg.importance = self.descriptor.importance msg.priority = self.descriptor.priority - ctx['msgId'] = rq.id - ctx['subject'] = msg.subject - ctx['appTitle'] = subject_prefix + ctx['_meta']['subject'] = msg.subject msg.from_mail = mail_from - msg.from_name = subject_prefix + msg.from_name = mail_name or self.descriptor.default_sender_name if self.html_template is not None: msg.html_body = self.html_template.render(ctx=ctx) if self.plain_template is not None: @@ -58,7 +60,7 @@ class TemplateRegistry: DESCRIPTOR_FILENAME = 'message.json' DESCRIPTOR_PATTERN = f'./**/{DESCRIPTOR_FILENAME}' - def __init__(self, cfg: MailerConfig, workdir: pathlib.Path, mode: str): + def __init__(self, cfg: MailerConfig, workdir: pathlib.Path): self.cfg = cfg self.workdir = workdir self.j2_env = jinja2.Environment( @@ -66,7 +68,7 @@ def __init__(self, cfg: MailerConfig, workdir: pathlib.Path, mode: str): extensions=['jinja2.ext.do'], ) self.templates = dict() # type: dict[str, MailTemplate] - self._load_templates(mode) + self._load_templates() def _load_jinja2(self, file_path: pathlib.Path) -> Optional[jinja2.Template]: if file_path.exists() and file_path.is_file(): @@ -129,19 +131,17 @@ def _load_template(self, path: pathlib.Path, template.html_images = [a for a in html_images if a is not None] return template - def _load_templates(self, mode: str): + def _load_templates(self): for descriptor_filename in self.workdir.glob(self.DESCRIPTOR_PATTERN): path = descriptor_filename.parent descriptor = self._load_descriptor(descriptor_filename) if descriptor is None: continue - if mode not in descriptor.modes: - continue template = self._load_template(path, descriptor) if template is None: continue LOG.info(f'Loaded template "{descriptor.id}" from {str(path)}') - self.templates[path.name] = template + self.templates[descriptor.id] = template def has_template_for(self, rq: MessageRequest) -> bool: return rq.template_name in self.templates.keys() diff --git a/packages/dsw-mailer/examples/msg.registry.registrationConfirmation.json b/packages/dsw-mailer/examples/msg.registry.registrationConfirmation.json index b9ae8217..c581768b 100644 --- a/packages/dsw-mailer/examples/msg.registry.registrationConfirmation.json +++ b/packages/dsw-mailer/examples/msg.registry.registrationConfirmation.json @@ -1,6 +1,6 @@ { "id": "2ff6a64c-b781-4aea-8873-2ccb3e9ee3d7", - "type": "registrationConfirmation", + "type": "registry:registrationConfirmation", "trigger": "file:msg.registry.registrationConfirmation.json", "ctx": { "email": "john.doe@example.com", diff --git a/packages/dsw-mailer/examples/msg.registry.registrationCreatedAnalytics.json b/packages/dsw-mailer/examples/msg.registry.registrationCreatedAnalytics.json index 250e5b1b..69932cea 100644 --- a/packages/dsw-mailer/examples/msg.registry.registrationCreatedAnalytics.json +++ b/packages/dsw-mailer/examples/msg.registry.registrationCreatedAnalytics.json @@ -1,6 +1,6 @@ { "id": "2ff6a64c-b781-4aea-8873-2ccb3e9ee3d7", - "type": "registrationCreatedAnalytics", + "type": "registry:registrationCreatedAnalytics", "trigger": "file:msg.registry.registrationCreatedAnalytics.json", "ctx": { "email": "john.doe@example.com", diff --git a/packages/dsw-mailer/examples/msg.registry.resetToken.json b/packages/dsw-mailer/examples/msg.registry.resetToken.json index 3233e1a3..45076e34 100644 --- a/packages/dsw-mailer/examples/msg.registry.resetToken.json +++ b/packages/dsw-mailer/examples/msg.registry.resetToken.json @@ -1,6 +1,6 @@ { "id": "2ff6a64c-b781-4aea-8873-2ccb3e9ee3d7", - "type": "resetToken", + "type": "registry:resetToken", "trigger": "file:msg.registry.resetToken.json", "ctx": { "email": "john.doe@example.com", diff --git a/packages/dsw-mailer/examples/msg.wizard.questionnaireInvitation.json b/packages/dsw-mailer/examples/msg.wizard.questionnaireInvitation.json index c58dc06e..3ee339c1 100644 --- a/packages/dsw-mailer/examples/msg.wizard.questionnaireInvitation.json +++ b/packages/dsw-mailer/examples/msg.wizard.questionnaireInvitation.json @@ -1,6 +1,6 @@ { "id": "2ff6a64c-b781-4aea-8873-2ccb3e9ee3d7", - "type": "questionnaireInvitation", + "type": "wizard:questionnaireInvitation", "trigger": "file:msg.wizard.questionnaireInvitation.json", "ctx": { "email": "john.doe@example.com", diff --git a/packages/dsw-mailer/examples/msg.wizard.registrationConfirmation.json b/packages/dsw-mailer/examples/msg.wizard.registrationConfirmation.json index af77f233..bc272401 100644 --- a/packages/dsw-mailer/examples/msg.wizard.registrationConfirmation.json +++ b/packages/dsw-mailer/examples/msg.wizard.registrationConfirmation.json @@ -1,6 +1,6 @@ { "id": "2ff6a64c-b781-4aea-8873-2ccb3e9ee3d7", - "type": "registrationConfirmation", + "type": "wizard:registrationConfirmation", "trigger": "file:msg.wizard.registrationConfirmation.json", "ctx": { "email": "john.doe@example.com", diff --git a/packages/dsw-mailer/examples/msg.wizard.registrationCreatedAnalytics.json b/packages/dsw-mailer/examples/msg.wizard.registrationCreatedAnalytics.json index fd99b7f9..7e9698d9 100644 --- a/packages/dsw-mailer/examples/msg.wizard.registrationCreatedAnalytics.json +++ b/packages/dsw-mailer/examples/msg.wizard.registrationCreatedAnalytics.json @@ -1,6 +1,6 @@ { "id": "2ff6a64c-b781-4aea-8873-2ccb3e9ee3d7", - "type": "registrationCreatedAnalytics", + "type": "wizard:registrationCreatedAnalytics", "trigger": "file:msg.wizard.registrationCreatedAnalytics.json", "ctx": { "email": "john.doe@example.com", diff --git a/packages/dsw-mailer/examples/msg.wizard.resetPassword.json b/packages/dsw-mailer/examples/msg.wizard.resetPassword.json index 4649b245..aeae397f 100644 --- a/packages/dsw-mailer/examples/msg.wizard.resetPassword.json +++ b/packages/dsw-mailer/examples/msg.wizard.resetPassword.json @@ -1,6 +1,6 @@ { "id": "2ff6a64c-b781-4aea-8873-2ccb3e9ee3d7", - "type": "resetPassword", + "type": "wizard:resetPassword", "trigger": "file:msg.wizard.resetPassword.json", "ctx": { "email": "john.doe@example.com", diff --git a/packages/dsw-mailer/templates/_common/footer.html.j2 b/packages/dsw-mailer/templates/_common/footer.html.j2 index 0c97765f..1a571aa2 100644 --- a/packages/dsw-mailer/templates/_common/footer.html.j2 +++ b/packages/dsw-mailer/templates/_common/footer.html.j2 @@ -155,8 +155,10 @@ Please do not reply to this email.

+ {% if ctx.appTitle and ctx.clientUrl %} This email was generated from {{ ctx.appTitle }}.
+ {% endif %} diff --git a/packages/dsw-mailer/templates/_common/footer.txt.j2 b/packages/dsw-mailer/templates/_common/footer.txt.j2 index ef4d9f37..4883ec8c 100644 --- a/packages/dsw-mailer/templates/_common/footer.txt.j2 +++ b/packages/dsw-mailer/templates/_common/footer.txt.j2 @@ -1,4 +1,4 @@ Thank you for using the DSW! __ -{{ ctx.appTitle }} +{% if ctx.appTitle %}{{ ctx.appTitle }}{% else %}Wizard team{% endif %} diff --git a/packages/dsw-mailer/templates/registry/registrationConfirmation/message.html.j2 b/packages/dsw-mailer/templates/registry/registrationConfirmation/message.html.j2 index 7c69f83c..f15b143c 100644 --- a/packages/dsw-mailer/templates/registry/registrationConfirmation/message.html.j2 +++ b/packages/dsw-mailer/templates/registry/registrationConfirmation/message.html.j2 @@ -1,5 +1,7 @@ {% extends '_common/layout.html.j2' %} {% block body %} +{% set registry_link = ctx.clientUrl ~ "/signup/" ~ ctx.organizationId ~ "/" ~ ctx.hash %} +{% set callback_link = ctx.clientUrl ~ "/signup/" ~ ctx.organizationId ~ "/" ~ ctx.hash %} @@ -17,7 +19,7 @@ @@ -42,7 +44,11 @@ @@ -68,7 +74,7 @@ diff --git a/packages/dsw-mailer/templates/registry/registrationConfirmation/message.json b/packages/dsw-mailer/templates/registry/registrationConfirmation/message.json index e3daef7b..8a91300a 100644 --- a/packages/dsw-mailer/templates/registry/registrationConfirmation/message.json +++ b/packages/dsw-mailer/templates/registry/registrationConfirmation/message.json @@ -1,6 +1,8 @@ { - "id": "registrationConfirmation", + "id": "registry:registrationConfirmation", "subject": "Registration Confirmation", + "subjectPrefix": true, + "defaultSenderName": "DSW Registry", "parts": [ { "type": "html", @@ -10,6 +12,5 @@ "type": "plain", "file": "message.txt.j2" } - ], - "modes": ["registry"] + ] } diff --git a/packages/dsw-mailer/templates/registry/registrationConfirmation/message.txt.j2 b/packages/dsw-mailer/templates/registry/registrationConfirmation/message.txt.j2 index 01213b02..a0e7ded8 100644 --- a/packages/dsw-mailer/templates/registry/registrationConfirmation/message.txt.j2 +++ b/packages/dsw-mailer/templates/registry/registrationConfirmation/message.txt.j2 @@ -1,5 +1,9 @@ -Dear {{ ctx.organization.name }}, +Dear {{ ctx.organizationName }}, -For the activation of your account, please proceed here: {{ ctx.activationLink }} +For the activation of your account, please proceed here: {% if ctx.callbackUrl -%} +{{ ctx.callbackUrl }}/registry/signup/{{ ctx.organizationId }}/{{ ctx.hash }} +{%- else -%} +{{ ctx.clientUrl }}/signup/{{ ctx.organizationId }}/{{ ctx.hash }} +{%- endif %} {% include '_common/footer.txt.j2' %} diff --git a/packages/dsw-mailer/templates/registry/registrationCreatedAnalytics/message.html.j2 b/packages/dsw-mailer/templates/registry/registrationCreatedAnalytics/message.html.j2 index f77d3b5f..cce8501b 100644 --- a/packages/dsw-mailer/templates/registry/registrationCreatedAnalytics/message.html.j2 +++ b/packages/dsw-mailer/templates/registry/registrationCreatedAnalytics/message.html.j2 @@ -17,18 +17,20 @@ diff --git a/packages/dsw-mailer/templates/registry/registrationCreatedAnalytics/message.json b/packages/dsw-mailer/templates/registry/registrationCreatedAnalytics/message.json index 63726d37..d3f435b8 100644 --- a/packages/dsw-mailer/templates/registry/registrationCreatedAnalytics/message.json +++ b/packages/dsw-mailer/templates/registry/registrationCreatedAnalytics/message.json @@ -1,6 +1,8 @@ { - "id": "registrationCreatedAnalytics", + "id": "registry:registrationCreatedAnalytics", "subject": "New Organization Registered", + "subjectPrefix": true, + "defaultSenderName": "DSW Registry", "parts": [ { "type": "html", @@ -10,6 +12,5 @@ "type": "plain", "file": "message.txt.j2" } - ], - "modes": ["registry"] + ] } diff --git a/packages/dsw-mailer/templates/registry/registrationCreatedAnalytics/message.txt.j2 b/packages/dsw-mailer/templates/registry/registrationCreatedAnalytics/message.txt.j2 index 917d0d16..6ecb9cdc 100644 --- a/packages/dsw-mailer/templates/registry/registrationCreatedAnalytics/message.txt.j2 +++ b/packages/dsw-mailer/templates/registry/registrationCreatedAnalytics/message.txt.j2 @@ -1,8 +1,9 @@ Hello! -Good news - we have a new organization in {{ ctx.appTitle }} ({{ ctx.clientUrl }}): +Good news - we have a new organization {% if ctx.appTitle %}in {{ ctx.appTitle }}{% endif %} ({{ ctx.clientUrl }}): -- Name: {{ ctx.organization.name }} -- Email: {{ ctx.organization.email }} +- ID: {{ ctx.organizationId }} +- Name: {{ ctx.organizationName }} +- Email: {{ ctx.organizationEmail }} {% include '_common/footer.txt.j2' %} diff --git a/packages/dsw-mailer/templates/registry/resetToken/message.html.j2 b/packages/dsw-mailer/templates/registry/resetToken/message.html.j2 index c70f5a96..f9c5a504 100644 --- a/packages/dsw-mailer/templates/registry/resetToken/message.html.j2 +++ b/packages/dsw-mailer/templates/registry/resetToken/message.html.j2 @@ -17,7 +17,7 @@ @@ -42,7 +42,7 @@ @@ -68,7 +68,7 @@ diff --git a/packages/dsw-mailer/templates/registry/resetToken/message.json b/packages/dsw-mailer/templates/registry/resetToken/message.json index 62f31af4..49286dce 100644 --- a/packages/dsw-mailer/templates/registry/resetToken/message.json +++ b/packages/dsw-mailer/templates/registry/resetToken/message.json @@ -1,6 +1,8 @@ { - "id": "resetToken", + "id": "registry:resetToken", "subject": "Reset Token", + "subjectPrefix": true, + "defaultSenderName": "DSW Registry", "parts": [ { "type": "html", @@ -10,6 +12,5 @@ "type": "plain", "file": "message.txt.j2" } - ], - "modes": ["registry"] + ] } diff --git a/packages/dsw-mailer/templates/registry/resetToken/message.txt.j2 b/packages/dsw-mailer/templates/registry/resetToken/message.txt.j2 index f6426534..ba8872d1 100644 --- a/packages/dsw-mailer/templates/registry/resetToken/message.txt.j2 +++ b/packages/dsw-mailer/templates/registry/resetToken/message.txt.j2 @@ -1,5 +1,5 @@ Hello, -You can set up a new token for your organization "{{ ctx.organization.name }}" here: {{ ctx.resetLink }} +You can set up a new token for your organization "{{ ctx.organizationName }}" ({{ ctx.organizationId }}) here: {{ self.clientUrl }}/forgotten-token/{{ ctx.organizationId }}/{{ ctx.hash }} {% include '_common/footer.txt.j2' %} diff --git a/packages/dsw-mailer/templates/wizard/questionnaireInvitation/message.html.j2 b/packages/dsw-mailer/templates/wizard/questionnaireInvitation/message.html.j2 index b20b4aa4..540f3769 100644 --- a/packages/dsw-mailer/templates/wizard/questionnaireInvitation/message.html.j2 +++ b/packages/dsw-mailer/templates/wizard/questionnaireInvitation/message.html.j2 @@ -17,8 +17,8 @@ @@ -43,7 +43,7 @@ @@ -72,7 +72,7 @@ If any problem occurs contact us via {{ ctx.supportEmail }}.

{% endif %} Thank you for using the DSW!

- Your {{ ctx.appTitle }} + {% if ctx.appTitle %}{{ ctx.appTitle }} team{% endif %}

diff --git a/packages/dsw-mailer/templates/wizard/questionnaireInvitation/message.json b/packages/dsw-mailer/templates/wizard/questionnaireInvitation/message.json index 11b1c5f1..033f9d9c 100644 --- a/packages/dsw-mailer/templates/wizard/questionnaireInvitation/message.json +++ b/packages/dsw-mailer/templates/wizard/questionnaireInvitation/message.json @@ -1,6 +1,8 @@ { - "id": "questionnaireInvitation", + "id": "wizard:questionnaireInvitation", "subject": "Project Invitation", + "subjectPrefix": true, + "defaultSenderName": "DSW", "parts": [ { "type": "html", @@ -10,6 +12,5 @@ "type": "plain", "file": "message.txt.j2" } - ], - "modes": ["wizard"] + ] } diff --git a/packages/dsw-mailer/templates/wizard/questionnaireInvitation/message.txt.j2 b/packages/dsw-mailer/templates/wizard/questionnaireInvitation/message.txt.j2 index 1647285b..97d7eab3 100644 --- a/packages/dsw-mailer/templates/wizard/questionnaireInvitation/message.txt.j2 +++ b/packages/dsw-mailer/templates/wizard/questionnaireInvitation/message.txt.j2 @@ -1,7 +1,7 @@ -Hi {{ ctx.invitee.firstName }}, +Hello {{ ctx.inviteeFirstName }}, -{{ ctx.owner.firstName}} {{ ctx.owner.lastName }} has invited you to the project "{{ ctx.project.name }}": +{{ ctx.ownerFirstName }} {{ ctx.ownerLastName }} has invited you to the project "{{ ctx.questionnaireName }}": -{{ ctx.project.link }} +{{ ctx.clientUrl }}/projects/{{ ctx.questionnaireUuid }} {% include '_common/footer.txt.j2' %} diff --git a/packages/dsw-mailer/templates/wizard/registrationConfirmation/message.html.j2 b/packages/dsw-mailer/templates/wizard/registrationConfirmation/message.html.j2 index 65626e43..fc7b2725 100644 --- a/packages/dsw-mailer/templates/wizard/registrationConfirmation/message.html.j2 +++ b/packages/dsw-mailer/templates/wizard/registrationConfirmation/message.html.j2 @@ -17,7 +17,7 @@ @@ -43,7 +43,7 @@ @@ -72,7 +72,7 @@ If any problem occurs contact us via {{ ctx.supportEmail }}.

{% endif %} Thank you for using the DSW!

- Your {{ ctx.appTitle }} + {% if ctx.appTitle %}{{ ctx.appTitle }} team{% endif %}

diff --git a/packages/dsw-mailer/templates/wizard/registrationConfirmation/message.json b/packages/dsw-mailer/templates/wizard/registrationConfirmation/message.json index 3fc35d1b..9fdafebb 100644 --- a/packages/dsw-mailer/templates/wizard/registrationConfirmation/message.json +++ b/packages/dsw-mailer/templates/wizard/registrationConfirmation/message.json @@ -1,6 +1,8 @@ { - "id": "registrationConfirmation", + "id": "wizard:registrationConfirmation", "subject": "Registration Confirmation", + "subjectPrefix": true, + "defaultSenderName": "DSW", "parts": [ { "type": "html", @@ -10,6 +12,5 @@ "type": "plain", "file": "message.txt.j2" } - ], - "modes": ["wizard"] + ] } diff --git a/packages/dsw-mailer/templates/wizard/registrationConfirmation/message.txt.j2 b/packages/dsw-mailer/templates/wizard/registrationConfirmation/message.txt.j2 index 674a4084..9de86a13 100644 --- a/packages/dsw-mailer/templates/wizard/registrationConfirmation/message.txt.j2 +++ b/packages/dsw-mailer/templates/wizard/registrationConfirmation/message.txt.j2 @@ -1,8 +1,8 @@ -Hi {{ ctx.user.firstName }}, +Hello {{ ctx.userFirstName }}, Welcome aboard! Well you’re almost set. Just one more step and you can start using the DSW. -To activate your account, please proceed here: {{ ctx.activationLink }} +To activate your account, please proceed here: {{ ctx.clientUrl }}/signup/{{ ctx.userUuid }}/{{ ctx.hash }} {% include '_common/footer.txt.j2' %} diff --git a/packages/dsw-mailer/templates/wizard/registrationCreatedAnalytics/message.html.j2 b/packages/dsw-mailer/templates/wizard/registrationCreatedAnalytics/message.html.j2 index bdbb9f09..a561201a 100644 --- a/packages/dsw-mailer/templates/wizard/registrationCreatedAnalytics/message.html.j2 +++ b/packages/dsw-mailer/templates/wizard/registrationCreatedAnalytics/message.html.j2 @@ -17,13 +17,13 @@ @@ -42,7 +42,7 @@ @@ -71,7 +71,7 @@ If any problem occurs contact us via {{ ctx.supportEmail }}.

{% endif %} Thank you for using the DSW!

- Your {{ ctx.appTitle }} + {% if ctx.appTitle %}{{ ctx.appTitle }} team{% endif %}

diff --git a/packages/dsw-mailer/templates/wizard/resetPassword/message.json b/packages/dsw-mailer/templates/wizard/resetPassword/message.json index d4ee64d8..f73cd26f 100644 --- a/packages/dsw-mailer/templates/wizard/resetPassword/message.json +++ b/packages/dsw-mailer/templates/wizard/resetPassword/message.json @@ -1,6 +1,8 @@ { - "id": "resetPassword", + "id": "wizard:resetPassword", "subject": "Reset Password", + "subjectPrefix": true, + "defaultSenderName": "DSW", "parts": [ { "type": "html", @@ -10,6 +12,5 @@ "type": "plain", "file": "message.txt.j2" } - ], - "modes": ["wizard"] + ] } diff --git a/packages/dsw-mailer/templates/wizard/resetPassword/message.txt.j2 b/packages/dsw-mailer/templates/wizard/resetPassword/message.txt.j2 index 1ea2be7d..9f9b1cc3 100644 --- a/packages/dsw-mailer/templates/wizard/resetPassword/message.txt.j2 +++ b/packages/dsw-mailer/templates/wizard/resetPassword/message.txt.j2 @@ -1,6 +1,6 @@ -Hi {{ ctx.user.firstName }}, +Hi {{ ctx.userFirstName }}, Forgot your password? -No problem, that happens to the best of us! You can easily set up a new password here: {{ ctx.resetLink }} +No problem, that happens to the best of us! You can easily set up a new password here: {{ ctx.clientUrl }}/forgotten-password/{{ ctx.userUuid }}/{{ ctx.hash }} {% include '_common/footer.txt.j2' %} diff --git a/packages/dsw-mailer/templates/wizard/twoFactorAuth/message.html.j2 b/packages/dsw-mailer/templates/wizard/twoFactorAuth/message.html.j2 index 57dfac9b..38838d67 100644 --- a/packages/dsw-mailer/templates/wizard/twoFactorAuth/message.html.j2 +++ b/packages/dsw-mailer/templates/wizard/twoFactorAuth/message.html.j2 @@ -16,8 +16,8 @@ @@ -70,7 +70,7 @@ If you did not initiate this login attempt, please contact us via {{ ctx.supportEmail }}. {% endif %}

Thank you for using the DSW!

- Your {{ ctx.appTitle }} + {% if ctx.appTitle %}{{ ctx.appTitle }} team{% endif %}

diff --git a/packages/dsw-mailer/templates/wizard/twoFactorAuth/message.json b/packages/dsw-mailer/templates/wizard/twoFactorAuth/message.json index b0f9e458..7dbde470 100644 --- a/packages/dsw-mailer/templates/wizard/twoFactorAuth/message.json +++ b/packages/dsw-mailer/templates/wizard/twoFactorAuth/message.json @@ -1,6 +1,8 @@ { - "id": "twoFactorAuth", + "id": "wizard:twoFactorAuth", "subject": "Two-Factor Login Verification", + "subjectPrefix": true, + "defaultSenderName": "DSW", "parts": [ { "type": "html", @@ -10,6 +12,5 @@ "type": "plain", "file": "message.txt.j2" } - ], - "modes": ["wizard"] + ] } diff --git a/packages/dsw-mailer/templates/wizard/twoFactorAuth/message.txt.j2 b/packages/dsw-mailer/templates/wizard/twoFactorAuth/message.txt.j2 index 3e8fa7e7..88ef6558 100644 --- a/packages/dsw-mailer/templates/wizard/twoFactorAuth/message.txt.j2 +++ b/packages/dsw-mailer/templates/wizard/twoFactorAuth/message.txt.j2 @@ -1,4 +1,4 @@ -Dear {{ ctx.user.firstName }}, +Dear {{ ctx.userFirstName }}, You have requested an authentication code to login into your account. Your code is:

Hello,

- Your registration of {{ ctx.organization.name }} is almost done. Just one more step to go. To activate the account, please click on the button below. + Your registration of {{ ctx.organizatioName }} ({{ ctx.organizationId }}) is almost done. Just one more step to go. To activate the account, please click on the button below.

- Activate your account + {% if ctx.callbackUrl %} + Activate your account + {% else %} + Activate your account + {% endif %}

Thank you for using the DSW!

- Your {{ ctx.appTitle }} + {% if ctx.appTitle %}{{ ctx.appTitle }} team{% endif %}

Hello!

- Good news — we have a new organization in {{ ctx.appTitle }} ({{ ctx.clientUrl }}): + Good news — we have a new organization {% if ctx.appTitle %}in {{ ctx.appTitle }}{% endif %} ({{ ctx.clientUrl }}):

+
ID
+
{{ ctx.organizationId }}
Name
-
{{ ctx.organization.name }}
+
{{ ctx.organizationName }}
Email
-
{{ ctx.organization.email }}
+
{{ ctx.organizationEmail }}

Have a nice day!

- Your {{ ctx.appTitle }} + {% if ctx.appTitle %}{{ ctx.appTitle }} team{% endif %}

Hello,

- Lost the token for {{ ctx.organization.name }}? No problem, just click the button below to set up a new one. + Lost the token for {{ ctx.organizationName }} ({{ ctx.organizationId }})? No problem, just click the button below to set up a new one.

- Set up a new password + Set up a new password

Thank you for using the DSW!

- Your {{ ctx.appTitle }} + {% if ctx.appTitle %}{{ ctx.appTitle }} team{% endif %}

- Hi {{ ctx.invitee.firstName }},

- {{ ctx.owner.firstName }} {{ ctx.lastName }} has invited you to the project {{ ctx.project.name}}. + Hello {{ ctx.inviteeFirstName }},

+ {{ ctx.ownerFirstName }} {{ ctx.ownerLastName }} has invited you to the project {{ ctx.questionnaireName }}.

- Open project + Open project

- Hi {{ ctx.user.firstName }},

+ Hello {{ ctx.userFirstName }},

Welcome aboard! Well you’re almost set. Just one more step and you can start using the DSW. To activate your account, please click on the button below.

- Activate your account + Activate your account

Hello!

- Good news — we have a new user in {{ ctx.appTitle }} ({{ ctx.clientUrl }}): + Good news — we have a new user {% if ctx.appTitle %}in {{ ctx.appTitle }} {% endif %}({{ ctx.clientUrl }}):

Name
-
{{ ctx.user.firstName }} {{ ctx.user.lastName }}
+
{{ ctx.userFirstName }} {{ ctx.userLastName }}
Email
-
{{ ctx.user.email }}
+
{{ ctx.userEmail }}

diff --git a/packages/dsw-mailer/templates/wizard/registrationCreatedAnalytics/message.json b/packages/dsw-mailer/templates/wizard/registrationCreatedAnalytics/message.json index 8f46ae4b..632aa5aa 100644 --- a/packages/dsw-mailer/templates/wizard/registrationCreatedAnalytics/message.json +++ b/packages/dsw-mailer/templates/wizard/registrationCreatedAnalytics/message.json @@ -1,6 +1,8 @@ { - "id": "registrationCreatedAnalytics", + "id": "wizard:registrationCreatedAnalytics", "subject": "New User Registered", + "subjectPrefix": true, + "defaultSenderName": "DSW", "parts": [ { "type": "html", @@ -10,6 +12,5 @@ "type": "plain", "file": "message.txt.j2" } - ], - "modes": ["wizard"] + ] } diff --git a/packages/dsw-mailer/templates/wizard/registrationCreatedAnalytics/message.txt.j2 b/packages/dsw-mailer/templates/wizard/registrationCreatedAnalytics/message.txt.j2 index 34a2a4de..db4cdf96 100644 --- a/packages/dsw-mailer/templates/wizard/registrationCreatedAnalytics/message.txt.j2 +++ b/packages/dsw-mailer/templates/wizard/registrationCreatedAnalytics/message.txt.j2 @@ -1,8 +1,8 @@ Hello! -Good news - we have a new user in our Wizard ({{ ctx.appTitle }}): +Good news - we have a new user in our Wizard ({% if ctx.appTitle %}{{ ctx.appTitle }}, {% endif %}{{ ctx.clientUrl }}): -- Name: {{ ctx.user.firstName }} {{ ctx.user.lastName }} -- Email: {{ ctx.user.email }} +- Name: {{ ctx.userFirstName }} {{ ctx.userLastName }} +- Email: {{ ctx.userEmail }} {% include '_common/footer.txt.j2' %} diff --git a/packages/dsw-mailer/templates/wizard/resetPassword/message.html.j2 b/packages/dsw-mailer/templates/wizard/resetPassword/message.html.j2 index dcfda09f..74a4a88c 100644 --- a/packages/dsw-mailer/templates/wizard/resetPassword/message.html.j2 +++ b/packages/dsw-mailer/templates/wizard/resetPassword/message.html.j2 @@ -16,7 +16,7 @@

- Hi {{ ctx.user.firstName }},

+ Hi {{ ctx.userFirstName }},

Forgot your password? No problem, that happens to the best of us! You can easily set up a new password by clicking on the button below.

- Set up a new password + Set up a new password

- Dear {{ ctx.user.firstName }},

- You have requested an authentication code to login into your account. Your code is: + Dear {{ ctx.userFirstName }},

+ You have requested an authentication code to log into your account. Your code is: