Example #1
0
    def notify_upload_permission(
        self,
        emitter: UserInContext,
        workspace_in_context: WorkspaceInContext,
        upload_permission_receivers: typing.List[UploadPermissionInContext],
        upload_permission_password: str,
    ) -> None:
        """
        Send mails to notify users for sharing content
        :param emitter: User emitter of the sharing
        :param workspace_in_context: workspace where receivers can now upload file
        :param upload_permission_receivers: list of upload_permission
        :param upload_permission_password: cleartext password of the sharing
        """

        email_sender = EmailSender(self.config, self._smtp_config,
                                   self.config.EMAIL__NOTIFICATION__ACTIVATED)
        upload_permission_password_enabled = False
        if upload_permission_password:
            upload_permission_password_enabled = True
        translator = Translator(self.config, default_lang=emitter.lang)
        message = self._notify_emitter(
            emitter=emitter,
            workspace_in_context=workspace_in_context,
            upload_permission_receivers=upload_permission_receivers,
            upload_permission_password=upload_permission_password,
            translator=translator,
        )
        send_email_through(config=self.config,
                           sendmail_callable=email_sender.send_mail,
                           message=message)
        emails_receivers_list = [
            upload_permission.email
            for upload_permission in upload_permission_receivers
        ]
        logger.info(
            self,
            'Generating upload permission mail from user "{}" to "{}"'.format(
                emitter.user_id, "".join(emails_receivers_list)),
        )
        for upload_permission in upload_permission_receivers:
            message = self._notify_receiver(
                emitter=emitter,
                workspace=workspace_in_context,
                upload_permission=upload_permission,
                upload_permission_password_enabled=
                upload_permission_password_enabled,
                translator=translator,
            )
            send_email_through(config=self.config,
                               sendmail_callable=email_sender.send_mail,
                               message=message)
Example #2
0
    def notify__share__content(
        self,
        emitter: User,
        shared_content: ContentInContext,
        content_share_receivers: typing.List[ContentShareInContext],
        share_password: str,
    ) -> None:
        """
        Send mails to notify users for sharing content
        :param emitter: User emitter of the sharing
        :param shared_content: content that is now shared
        :param content_share_receivers: list of content share
        :param share_password: cleartext password of the sharing
        """

        email_sender = EmailSender(self.config, self._smtp_config,
                                   self.config.EMAIL__NOTIFICATION__ACTIVATED)
        share_password_enabled = False
        if share_password:
            share_password_enabled = True
        translator = Translator(self.config, default_lang=emitter.lang)
        message = self._notify_emitter(
            emitter=emitter,
            shared_content=shared_content,
            content_share_receivers=content_share_receivers,
            share_password=share_password,
            translator=translator,
        )
        send_email_through(config=self.config,
                           sendmail_callable=email_sender.send_mail,
                           message=message)
        for content_share in content_share_receivers:
            emails_receivers_list = [
                share_content.email
                for share_content in content_share_receivers
            ]
            logger.info(
                self,
                'Generating share mail from user "{}" to "{}"'.format(
                    emitter.user_id, "".join(emails_receivers_list)),
            )
            message = self._notify_receiver(
                emitter=emitter,
                shared_content=shared_content,
                content_share=content_share,
                share_password_enabled=share_password_enabled,
                translator=translator,
            )
            send_email_through(config=self.config,
                               sendmail_callable=email_sender.send_mail,
                               message=message)
Example #3
0
    def notify_reset_password(self, user: User,
                              reset_password_token: str) -> None:
        """
        Reset password link for user
        :param user: user to notify
        :param reset_password_token: token for resetting password
        """
        logger.debug(self, "user: {}".format(user.user_id))
        logger.info(self,
                    "Generating reset password email to {}".format(user.email))
        translator = Translator(self.config, default_lang=user.lang)
        email_sender = EmailSender(self.config, self._smtp_config,
                                   self.config.EMAIL__NOTIFICATION__ACTIVATED)
        translated_subject = translator.get_translation(
            self.config.EMAIL__NOTIFICATION__RESET_PASSWORD_REQUEST__SUBJECT)
        subject = translated_subject.replace(EST.WEBSITE_TITLE,
                                             str(self.config.WEBSITE__TITLE))
        from_header = self._get_sender()
        to_header = EmailAddress(user.get_display_name(), user.email)

        html_template_file_path = (
            self.config.
            EMAIL__NOTIFICATION__RESET_PASSWORD_REQUEST__TEMPLATE__HTML)
        # TODO - G.M - 2018-08-17 - Generate token
        context = {
            "user":
            user,
            "logo_url":
            get_email_logo_frontend_url(self.config),
            "reset_password_url":
            get_reset_password_frontend_url(self.config,
                                            token=reset_password_token,
                                            email=user.email),
        }

        body_html = self._render_template(
            mako_template_filepath=html_template_file_path,
            context=context,
            translator=translator)
        message = EmailNotificationMessage(
            subject=subject,
            from_header=from_header,
            to_header=to_header,
            body_html=body_html,
            lang=translator.default_lang,
        )
        send_email_through(config=self.config,
                           sendmail_callable=email_sender.send_mail,
                           message=message)
Example #4
0
    def notify_created_account(
            self,
            user: User,
            password: str,
            origin_user: typing.Optional[User] = None) -> None:
        """
        Send created account email to given user.

        :param password: choosed password
        :param user: user to notify
        """
        logger.info(self,
                    "Generating created account mail to {}".format(user.email))

        email_sender = EmailSender(self.config, self._smtp_config,
                                   self.config.EMAIL__NOTIFICATION__ACTIVATED)
        translator = Translator(self.config, default_lang=user.lang)
        translated_subject = translator.get_translation(
            self.config.EMAIL__NOTIFICATION__CREATED_ACCOUNT__SUBJECT)
        subject = translated_subject.replace(EST.WEBSITE_TITLE,
                                             str(self.config.WEBSITE__TITLE))
        from_header = self._get_sender(origin_user)
        to_header = EmailAddress(user.get_display_name(), user.email)
        html_template_file_path = self.config.EMAIL__NOTIFICATION__CREATED_ACCOUNT__TEMPLATE__HTML

        context = {
            "origin_user": origin_user,
            "user": user,
            "password": password,
            "logo_url": get_email_logo_frontend_url(self.config),
            "login_url": get_login_frontend_url(self.config),
        }
        translator = Translator(self.config, default_lang=user.lang)
        body_html = self._render_template(
            mako_template_filepath=html_template_file_path,
            context=context,
            translator=translator)
        message = EmailNotificationMessage(
            subject=subject,
            from_header=from_header,
            to_header=to_header,
            body_html=body_html,
            lang=translator.default_lang,
        )

        send_email_through(config=self.config,
                           sendmail_callable=email_sender.send_mail,
                           message=message)
Example #5
0
    def notify_reset_password(self, user: User, reset_password_token: str) -> None:
        """
        Reset password link for user
        :param user: user to notify
        :param reset_password_token: token for resetting password
        """
        logger.debug(self, "user: {}".format(user.user_id))
        logger.info(self, "Generating reset password email to {}".format(user.email))
        translator = Translator(self.config, default_lang=user.lang)
        email_sender = EmailSender(
            self.config, self._smtp_config, self.config.EMAIL__NOTIFICATION__ACTIVATED
        )
        translated_subject = translator.get_translation(
            self.config.EMAIL__NOTIFICATION__RESET_PASSWORD_REQUEST__SUBJECT
        )
        subject = translated_subject.replace(EST.WEBSITE_TITLE, str(self.config.WEBSITE__TITLE))
        message = MIMEMultipart("alternative")
        message["Subject"] = subject
        message["From"] = self._get_sender()
        message["To"] = formataddr((user.get_display_name(), user.email))

        html_template_file_path = (
            self.config.EMAIL__NOTIFICATION__RESET_PASSWORD_REQUEST__TEMPLATE__HTML
        )
        # TODO - G.M - 2018-08-17 - Generate token
        context = {
            "user": user,
            "logo_url": get_email_logo_frontend_url(self.config),
            "reset_password_url": get_reset_password_frontend_url(
                self.config, token=reset_password_token, email=user.email
            ),
        }

        body_html = self._render_template(
            mako_template_filepath=html_template_file_path, context=context, translator=translator
        )

        part2 = MIMEText(body_html, "html", "utf-8")

        # Attach parts into message container.
        # According to RFC 2046, the last part of a multipart message,
        # in this case the HTML message, is best and preferred.
        message.attach(part2)

        send_email_through(
            config=self.config, sendmail_callable=email_sender.send_mail, message=message
        )
Example #6
0
    def notify_created_account(self, user: User, password: str) -> None:
        """
        Send created account email to given user.

        :param password: choosed password
        :param user: user to notify
        """
        logger.info(self, "Generating created account mail to {}".format(user.email))

        email_sender = EmailSender(
            self.config, self._smtp_config, self.config.EMAIL__NOTIFICATION__ACTIVATED
        )
        translator = Translator(self.config, default_lang=user.lang)
        translated_subject = translator.get_translation(
            self.config.EMAIL__NOTIFICATION__CREATED_ACCOUNT__SUBJECT
        )
        subject = translated_subject.replace(EST.WEBSITE_TITLE, str(self.config.WEBSITE__TITLE))
        message = MIMEMultipart("alternative")
        message["Subject"] = subject
        message["From"] = self._get_sender()
        message["To"] = formataddr((user.get_display_name(), user.email))

        html_template_file_path = self.config.EMAIL__NOTIFICATION__CREATED_ACCOUNT__TEMPLATE__HTML

        context = {
            "user": user,
            "password": password,
            "logo_url": get_email_logo_frontend_url(self.config),
            "login_url": get_login_frontend_url(self.config),
        }
        translator = Translator(self.config, default_lang=user.lang)
        body_html = self._render_template(
            mako_template_filepath=html_template_file_path, context=context, translator=translator
        )

        part2 = MIMEText(body_html, "html", "utf-8")

        # Attach parts into message container.
        # According to RFC 2046, the last part of a multipart message,
        # in this case the HTML message, is best and preferred.
        message.attach(part2)

        send_email_through(
            config=self.config, sendmail_callable=email_sender.send_mail, message=message
        )
Example #7
0
    def notify_new_upload(
        self,
        uploader_username: str,
        uploader_email: str,
        uploader_message: typing.Optional[str],
        workspace_in_context: WorkspaceInContext,
        uploaded_contents: typing.List[ContentInContext],
    ) -> None:
        email_sender = EmailSender(self.config, self._smtp_config,
                                   self.config.EMAIL__NOTIFICATION__ACTIVATED)
        notifiable_roles = WorkspaceApi(
            current_user=None, session=self.session,
            config=self.config).get_notifiable_roles(
                workspace_in_context.workspace, force_notify=True)

        for role in notifiable_roles:
            logger.info(
                self,
                'Generating new upload notification in workspace "{}" from "{}" to "{}"'
                .format(workspace_in_context.workspace_id, uploader_username,
                        role.user.email),
            )
            translator = Translator(app_config=self.config,
                                    default_lang=role.user.lang)
            uploader = EmailUser(username=uploader_username,
                                 user_email=uploader_email)
            message = self._notify_new_upload(
                workspace_in_context=workspace_in_context,
                receiver=role.user,
                uploader=uploader,
                translator=translator,
                uploaded_contents=uploaded_contents,
                uploader_message=uploader_message,
            )
            send_email_through(config=self.config,
                               sendmail_callable=email_sender.send_mail,
                               message=message)
Example #8
0
    def notify_content_update(
            self,
            event_actor_id: int,
            event_content_id: int
    ) -> None:
        """
        Look for all users to be notified about the new content and send them an
        individual email
        :param event_actor_id: id of the user that has triggered the event
        :param event_content_id: related content_id
        :return:
        """
        # FIXME - D.A. - 2014-11-05
        # Dirty import. It's here in order to avoid circular import
        from tracim_backend.lib.core.content import ContentApi
        from tracim_backend.lib.core.user import UserApi
        user = UserApi(
            None,
            config=self.config,
            session=self.session,
            ).get_one(event_actor_id)
        logger.debug(self, 'Content: {}'.format(event_content_id))
        content_api = ContentApi(
            current_user=user,
            session=self.session,
            config=self.config,
            )
        content = ContentApi(
            session=self.session,
            current_user=user,  # nopep8 TODO - use a system user instead of the user that has triggered the event
            config=self.config,
            show_archived=True,
            show_deleted=True,
        ).get_one(event_content_id, content_type_list.Any_SLUG)
        workspace_api = WorkspaceApi(
            session=self.session,
            current_user=user,
            config=self.config,
        )
        workpace_in_context = workspace_api.get_workspace_with_context(workspace_api.get_one(content.workspace_id))  # nopep8
        main_content = content.parent if content.type == content_type_list.Comment.slug else content  # nopep8
        notifiable_roles = WorkspaceApi(
            current_user=user,
            session=self.session,
            config=self.config,
        ).get_notifiable_roles(content.workspace)

        if len(notifiable_roles) <= 0:
            logger.info(self, 'Skipping notification as nobody subscribed to in workspace {}'.format(content.workspace.label))
            return


        logger.info(self, 'Generating content {} notification email for {} user(s)'.format(
            content.content_id,
            len(notifiable_roles)
        ))
        # INFO - D.A. - 2014-11-06
        # The following email sender will send emails in the async task queue
        # This allow to build all mails through current thread but really send them (including SMTP connection)
        # In the other thread.
        #
        # This way, the webserver will return sooner (actually before notification emails are sent
        email_sender = EmailSender(
            self.config,
            self._smtp_config,
            self.config.EMAIL_NOTIFICATION_ACTIVATED
        )
        for role in notifiable_roles:
            logger.info(self,
                        'Generating content {} notification email to {}'.format(
                            content.content_id,
                            role.user.email
                        )
            )
            translator = Translator(app_config=self.config, default_lang=role.user.lang)  # nopep8
            _ = translator.get_translation
            to_addr = formataddr((role.user.display_name, role.user.email))
            # INFO - G.M - 2017-11-15 - set content_id in header to permit reply
            # references can have multiple values, but only one in this case.
            replyto_addr = self.config.EMAIL_NOTIFICATION_REPLY_TO_EMAIL.replace( # nopep8
                '{content_id}', str(main_content.content_id)
            )

            reference_addr = self.config.EMAIL_NOTIFICATION_REFERENCES_EMAIL.replace( #nopep8
                '{content_id}',str(main_content.content_id)
             )
            #
            #  INFO - D.A. - 2014-11-06
            # We do not use .format() here because the subject defined in the .ini file
            # may not include all required labels. In order to avoid partial format() (which result in an exception)
            # we do use replace and force the use of .__str__() in order to process LazyString objects
            #
            content_status = translator.get_translation(main_content.get_status().label)
            translated_subject = translator.get_translation(self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_SUBJECT)
            subject = translated_subject.replace(EST.WEBSITE_TITLE, self.config.WEBSITE_TITLE.__str__())
            subject = subject.replace(EST.WORKSPACE_LABEL, main_content.workspace.label.__str__())
            subject = subject.replace(EST.CONTENT_LABEL, main_content.label.__str__())
            subject = subject.replace(EST.CONTENT_STATUS_LABEL, content_status)
            reply_to_label = _('{username} & all members of {workspace}').format(  # nopep8
                username=user.display_name,
                workspace=main_content.workspace.label)

            message = MIMEMultipart('alternative')
            message['Subject'] = subject
            message['From'] = self._get_sender(user)
            message['To'] = to_addr
            message['Reply-to'] = formataddr((reply_to_label, replyto_addr))
            # INFO - G.M - 2017-11-15
            # References can theorically have label, but in pratice, references
            # contains only message_id from parents post in thread.
            # To link this email to a content we create a virtual parent
            # in reference who contain the content_id.
            message['References'] = formataddr(('', reference_addr))
            content_in_context = content_api.get_content_in_context(content)
            parent_in_context = None
            if content.parent_id:
                parent_in_context = content_api.get_content_in_context(content.parent) # nopep8

            body_html = self._build_email_body_for_content(
                self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_HTML,
                role,
                content_in_context,
                parent_in_context,
                workpace_in_context,
                user,
                translator,
            )

            part2 = MIMEText(body_html, 'html', 'utf-8')
            # Attach parts into message container.
            # According to RFC 2046, the last part of a multipart message, in this case
            # the HTML message, is best and preferred.
            message.attach(part2)

            self.log_email_notification(
                msg='an email was created to {}'.format(message['To']),
                action='{:8s}'.format('CREATED'),
                email_recipient=message['To'],
                email_subject=message['Subject'],
                config=self.config,
            )

            send_email_through(
                self.config,
                email_sender.send_mail,
                message
            )
Example #9
0
    def notify_content_update(self, event_actor_id: int,
                              event_content_id: int) -> None:
        """
        Look for all users to be notified about the new content and send them an
        individual email
        :param event_actor_id: id of the user that has triggered the event
        :param event_content_id: related content_id
        :return:
        """
        # FIXME - D.A. - 2014-11-05
        # Dirty import. It's here in order to avoid circular import
        from tracim_backend.lib.core.content import ContentApi
        from tracim_backend.lib.core.user import UserApi

        user = UserApi(None, config=self.config,
                       session=self.session).get_one(event_actor_id)
        logger.debug(self, "Content: {}".format(event_content_id))
        content_api = ContentApi(current_user=user,
                                 session=self.session,
                                 config=self.config)
        content = ContentApi(
            session=self.session,
            current_user=user,
            # TODO - G.M - 2019-04-24 - use a system user instead of the user that has triggered the event
            config=self.config,
            show_archived=True,
            show_deleted=True,
        ).get_one(event_content_id, content_type_list.Any_SLUG)
        workspace_api = WorkspaceApi(session=self.session,
                                     current_user=user,
                                     config=self.config)
        workpace_in_context = workspace_api.get_workspace_with_context(
            workspace_api.get_one(content.workspace_id))
        main_content = content.parent if content.type == content_type_list.Comment.slug else content
        notifiable_roles = WorkspaceApi(
            current_user=user, session=self.session,
            config=self.config).get_notifiable_roles(content.workspace)

        if len(notifiable_roles) <= 0:
            logger.info(
                self,
                "Skipping notification as nobody subscribed to in workspace {}"
                .format(content.workspace.label),
            )
            return

        logger.info(
            self,
            "Generating content {} notification email for {} user(s)".format(
                content.content_id, len(notifiable_roles)),
        )
        # INFO - D.A. - 2014-11-06
        # The following email sender will send emails in the async task queue
        # This allow to build all mails through current thread but really send them (including SMTP connection)
        # In the other thread.
        #
        # This way, the webserver will return sooner (actually before notification emails are sent
        email_sender = EmailSender(self.config, self._smtp_config,
                                   self.config.EMAIL__NOTIFICATION__ACTIVATED)
        for role in notifiable_roles:
            logger.info(
                self,
                "Generating content {} notification email to {}".format(
                    content.content_id, role.user.email),
            )
            translator = Translator(app_config=self.config,
                                    default_lang=role.user.lang)
            _ = translator.get_translation
            # INFO - G.M - 2017-11-15 - set content_id in header to permit reply
            # references can have multiple values, but only one in this case.
            replyto_addr = self.config.EMAIL__NOTIFICATION__REPLY_TO__EMAIL.replace(
                "{content_id}", str(main_content.content_id))

            reference_addr = self.config.EMAIL__NOTIFICATION__REFERENCES__EMAIL.replace(
                "{content_id}", str(main_content.content_id))
            #
            #  INFO - D.A. - 2014-11-06
            # We do not use .format() here because the subject defined in the .ini file
            # may not include all required labels. In order to avoid partial format() (which result in an exception)
            # we do use replace and force the use of .__str__() in order to process LazyString objects
            #
            content_status = translator.get_translation(
                main_content.get_status().label)
            translated_subject = translator.get_translation(
                self.config.EMAIL__NOTIFICATION__CONTENT_UPDATE__SUBJECT)
            subject = translated_subject.replace(
                EST.WEBSITE_TITLE, self.config.WEBSITE__TITLE.__str__())
            subject = subject.replace(EST.WORKSPACE_LABEL,
                                      main_content.workspace.label.__str__())
            subject = subject.replace(EST.CONTENT_LABEL,
                                      main_content.label.__str__())
            subject = subject.replace(EST.CONTENT_STATUS_LABEL, content_status)
            reply_to_label = _(
                "{username} & all members of {workspace}").format(
                    username=user.display_name,
                    workspace=main_content.workspace.label)

            content_in_context = content_api.get_content_in_context(content)
            parent_in_context = None
            if content.parent_id:
                parent_in_context = content_api.get_content_in_context(
                    content.parent)

            body_html = self._build_email_body_for_content(
                self.config.
                EMAIL__NOTIFICATION__CONTENT_UPDATE__TEMPLATE__HTML,
                role,
                content_in_context,
                parent_in_context,
                workpace_in_context,
                user,
                translator,
            )

            message = EmailNotificationMessage(
                subject=subject,
                from_header=self._get_sender(user),
                to_header=EmailAddress(role.user.display_name,
                                       role.user.email),
                reply_to=EmailAddress(reply_to_label, replyto_addr),
                # INFO - G.M - 2017-11-15
                # References can theorically have label, but in pratice, references
                # contains only message_id from parents post in thread.
                # To link this email to a content we create a virtual parent
                # in reference who contain the content_id.
                # INFO - G.M - 2020-04-03 - Enforce angle bracket in references header
                # we need that to ensure best software compatibility
                # compat from parsing software
                references=EmailAddress("",
                                        reference_addr,
                                        force_angle_bracket=True),
                body_html=body_html,
                lang=translator.default_lang,
            )

            self.log_email_notification(
                msg="an email was created to {}".format(message["To"]),
                action="{:8s}".format("CREATED"),
                email_recipient=message["To"],
                email_subject=message["Subject"],
                config=self.config,
            )

            send_email_through(self.config, email_sender.send_mail, message)
Example #10
0
    def notify_reset_password(
            self,
            user: User,
            reset_password_token: str,
    ) -> None:
        """
        Reset password link for user
        :param user: user to notify
        :param reset_password_token: token for resetting password
        """
        logger.debug(self, 'user: {}'.format(user.user_id))
        logger.info(self, 'Sending asynchronous email to 1 user ({0})'.format(
            user.email,
        ))
        translator = Translator(self.config, default_lang=user.lang)
        async_email_sender = EmailSender(
            self.config,
            self._smtp_config,
            self.config.EMAIL_NOTIFICATION_ACTIVATED
        )
        subject = self.config.EMAIL_NOTIFICATION_RESET_PASSWORD_SUBJECT.replace(
            EST.WEBSITE_TITLE,
            str(self.config.WEBSITE_TITLE)
        )
        message = MIMEMultipart('alternative')
        message['Subject'] = subject
        message['From'] = self._get_sender()
        message['To'] = formataddr((user.get_display_name(), user.email))

        text_template_file_path = self.config.EMAIL_NOTIFICATION_RESET_PASSWORD_TEMPLATE_TEXT  # nopep8
        html_template_file_path = self.config.EMAIL_NOTIFICATION_RESET_PASSWORD_TEMPLATE_HTML  # nopep8
        # TODO - G.M - 2018-08-17 - Generate token
        context = {
            'user': user,
            'logo_url': get_email_logo_frontend_url(self.config),
            'reset_password_url': get_reset_password_frontend_url(
                self.config,
                token=reset_password_token,
                email=user.email,
            ),
        }
        body_text = self._render_template(
            mako_template_filepath=text_template_file_path,
            context=context,
            translator=translator,
        )

        body_html = self._render_template(
            mako_template_filepath=html_template_file_path,
            context=context,
            translator=translator,
        )

        part1 = MIMEText(body_text, 'plain', 'utf-8')
        part2 = MIMEText(body_html, 'html', 'utf-8')

        # Attach parts into message container.
        # According to RFC 2046, the last part of a multipart message,
        # in this case the HTML message, is best and preferred.
        message.attach(part1)
        message.attach(part2)

        send_email_through(
            config=self.config,
            sendmail_callable=async_email_sender.send_mail,
            message=message
        )
Example #11
0
    def notify_created_account(
            self,
            user: User,
            password: str,
    ) -> None:
        """
        Send created account email to given user.

        :param password: choosed password
        :param user: user to notify
        """
        # TODO BS 20160712: Cyclic import
        logger.debug(self, 'user: {}'.format(user.user_id))
        logger.info(self, 'Sending asynchronous email to 1 user ({0})'.format(
            user.email,
        ))

        async_email_sender = EmailSender(
            self.config,
            self._smtp_config,
            self.config.EMAIL_NOTIFICATION_ACTIVATED
        )

        subject = \
            self.config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_SUBJECT \
            .replace(
                EST.WEBSITE_TITLE,
                str(self.config.WEBSITE_TITLE)
            )
        message = MIMEMultipart('alternative')
        message['Subject'] = subject
        message['From'] = self._get_sender()
        message['To'] = formataddr((user.get_display_name(), user.email))

        text_template_file_path = self.config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_TEXT  # nopep8
        html_template_file_path = self.config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_HTML  # nopep8

        context = {
            'user': user,
            'password': password,
            'logo_url': get_email_logo_frontend_url(self.config),
            'login_url': get_login_frontend_url(self.config),
        }
        translator = Translator(self.config, default_lang=user.lang)
        body_text = self._render_template(
            mako_template_filepath=text_template_file_path,
            context=context,
            translator=translator
        )

        body_html = self._render_template(
            mako_template_filepath=html_template_file_path,
            context=context,
            translator=translator
        )

        part1 = MIMEText(body_text, 'plain', 'utf-8')
        part2 = MIMEText(body_html, 'html', 'utf-8')

        # Attach parts into message container.
        # According to RFC 2046, the last part of a multipart message,
        # in this case the HTML message, is best and preferred.
        message.attach(part1)
        message.attach(part2)

        send_email_through(
            config=self.config,
            sendmail_callable=async_email_sender.send_mail,
            message=message
        )