Exemplo n.º 1
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)
Exemplo n.º 2
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)
Exemplo n.º 3
0
 def __init__(
     self,
     session: TracimSession,
     current_user: typing.Optional[User],
     config: CFG,
     force_role: bool = False,
     show_deleted: bool = False,
     access_types_filter: typing.Optional[
         typing.List[WorkspaceAccessType]] = None,
 ):
     """
     :param current_user: Current user of context
     :param force_role: If True, app role in queries even if admin
     """
     session.assert_event_mechanism()
     self._session = session
     self._user = current_user
     self._config = config
     self._force_role = force_role
     self.show_deleted = show_deleted
     self._access_types_filter = access_types_filter
     default_lang = None
     if self._user:
         default_lang = self._user.lang
     self.translator = Translator(app_config=self._config,
                                  default_lang=default_lang)
Exemplo n.º 4
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
        )
Exemplo n.º 5
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
        )
Exemplo n.º 6
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)
Exemplo n.º 7
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)
Exemplo n.º 8
0
 def _notify_receiver(
     self,
     emitter: User,
     workspace: Workspace,
     upload_permission: UploadPermissionInContext,
     upload_permission_password_enabled: bool,
     translator: Translator,
 ) -> Message:
     logger.info(
         self,
         'preparing email from user "{}" for the upload permission on workspace "{}" to "{}"'
         .format(emitter.user_id, workspace.workspace_id,
                 upload_permission.email),
     )
     translated_subject = translator.get_translation(
         self.config.
         EMAIL__NOTIFICATION__UPLOAD_PERMISSION_TO_RECEIVER__SUBJECT)
     subject = translated_subject.format(
         website_title=self.config.WEBSITE__TITLE,
         emitter_name=emitter.display_name)
     from_header = self._get_sender(emitter)
     to_header = EmailAddress.from_rfc_email_address(
         upload_permission.email)
     html_template_file_path = (
         self.config.
         EMAIL__NOTIFICATION__UPLOAD_PERMISSION_TO_RECEIVER__TEMPLATE__HTML)
     receiver = EmailUser(user_email=upload_permission.email)
     context = {
         "emitter":
         emitter,
         "workspace":
         workspace,
         "upload_permission":
         upload_permission,
         "receiver":
         receiver,
         "upload_permission_password_enabled":
         upload_permission_password_enabled,
     }
     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,
     )
     return message
Exemplo n.º 9
0
    def _notify_receiver(
        self,
        emitter: User,
        shared_content: ContentInContext,
        content_share: ContentShareInContext,
        share_password_enabled: bool,
        translator: Translator,
    ) -> Message:
        logger.info(
            self,
            'preparing email from user "{}" for the share on content "{}" to "{}"'
            .format(emitter.user_id, shared_content.content_id,
                    content_share.email),
        )
        translated_subject = translator.get_translation(
            self.config.EMAIL__NOTIFICATION__SHARE_CONTENT_TO_RECEIVER__SUBJECT
        )
        subject = translated_subject.format(
            website_title=self.config.WEBSITE__TITLE,
            content_filename=shared_content.filename,
            emitter_name=emitter.display_name,
        )
        from_header = self._get_sender(emitter)
        to_header = EmailAddress.from_rfc_email_address(content_share.email)
        username, address = email.utils.parseaddr(content_share.email)
        html_template_file_path = (
            self.config.
            EMAIL__NOTIFICATION__SHARE_CONTENT_TO_RECEIVER__TEMPLATE__HTML)
        receiver = EmailUser(user_email=content_share.email)
        context = {
            "emitter": emitter,
            "shared_content": shared_content,
            "content_share": content_share,
            "share_password_enabled": share_password_enabled,
            "receiver": receiver,
        }
        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,
        )
        return message
Exemplo n.º 10
0
    def _notify_emitter(
        self,
        emitter: UserInContext,
        workspace_in_context: WorkspaceInContext,
        upload_permission_receivers: typing.List[UploadPermissionInContext],
        upload_permission_password: str,
        translator: Translator,
    ) -> Message:
        logger.info(
            self,
            'preparing email to user "{}" about upload_permission on workspace "{}" info created'
            .format(emitter.user_id, workspace_in_context.workspace_id),
        )
        translated_subject = translator.get_translation(
            self.config.
            EMAIL__NOTIFICATION__UPLOAD_PERMISSION_TO_EMITTER__SUBJECT)
        subject = translated_subject.format(
            website_title=self.config.WEBSITE__TITLE,
            nb_receivers=len(upload_permission_receivers),
            workspace_name=workspace_in_context.label,
        )
        from_header = self._get_sender()
        to_header = EmailAddress(emitter.display_name, emitter.email)
        html_template_file_path = (
            self.config.
            EMAIL__NOTIFICATION__UPLOAD_PERMISSION_TO_EMITTER__TEMPLATE__HTML)
        context = {
            "emitter": emitter,
            "workspace": workspace_in_context,
            "upload_permission_receivers": upload_permission_receivers,
            "upload_permission_password": upload_permission_password,
        }
        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,
        )
        return message
Exemplo n.º 11
0
    def _notify_emitter(
        self,
        emitter: User,
        shared_content: ContentInContext,
        content_share_receivers: typing.List[ContentShareInContext],
        share_password: str,
        translator: Translator,
    ) -> Message:
        logger.info(
            self,
            'preparing email to user "{}" about share on content "{}" info created'.format(
                emitter.user_id, shared_content.content_id
            ),
        )
        translated_subject = translator.get_translation(
            self.config.EMAIL__NOTIFICATION__SHARE_CONTENT_TO_EMITTER__SUBJECT
        )
        subject = translated_subject.format(
            website_title=self.config.WEBSITE__TITLE,
            content_filename=shared_content.filename,
            nb_receivers=len(content_share_receivers),
        )
        from_header = self._get_sender()
        to_header = EmailAddress(emitter.display_name, emitter.email)
        html_template_file_path = (
            self.config.EMAIL__NOTIFICATION__SHARE_CONTENT_TO_EMITTER__TEMPLATE__HTML
        )
        context = {
            "emitter": emitter,
            "shared_content": shared_content,
            "content_share_receivers": content_share_receivers,
            "share_password": share_password,
        }
        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,
        )

        return message
Exemplo n.º 12
0
 def _notify_new_upload(
     self,
     workspace_in_context: WorkspaceInContext,
     receiver: UserInContext,
     uploader: EmailUser,
     uploader_message: typing.Optional[str],
     uploaded_contents: typing.List[ContentInContext],
     translator: Translator,
 ) -> Message:
     logger.info(
         self,
         'preparing email to user "{}" about new upload on workspace "{}" info created'
         .format(receiver.user_id, workspace_in_context.workspace_id),
     )
     translated_subject = translator.get_translation(
         self.config.EMAIL__NOTIFICATION__NEW_UPLOAD_EVENT__SUBJECT)
     subject = translated_subject.format(
         website_title=self.config.WEBSITE__TITLE,
         uploader_username=uploader.username,
         nb_uploaded_contents=len(uploaded_contents),
         workspace_name=workspace_in_context.label,
     )
     from_header = self._get_sender()
     to_header = EmailAddress(receiver.display_name, receiver.email)
     html_template_file_path = self.config.EMAIL__NOTIFICATION__NEW_UPLOAD_EVENT__TEMPLATE__HTML
     context = {
         "receiver": receiver,
         "workspace": workspace_in_context,
         "uploader": uploader,
         "uploaded_contents": uploaded_contents,
         "uploader_message": uploader_message,
     }
     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,
     )
     return message
Exemplo n.º 13
0
 def __init__(
     self,
     session: Session,
     current_user: User,
     config: CFG,
     force_role: bool = False,
     show_deleted: bool = False,
 ):
     """
     :param current_user: Current user of context
     :param force_role: If True, app role in queries even if admin
     """
     self._session = session
     self._user = current_user
     self._config = config
     self._force_role = force_role
     self.show_deleted = show_deleted
     default_lang = None
     if self._user:
         default_lang = self._user.lang
     self.translator = Translator(app_config=self._config,
                                  default_lang=default_lang)
Exemplo n.º 14
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)
Exemplo n.º 15
0
    def upload_files(
        self,
        upload_permission: UploadPermission,
        uploader_username: str,
        message: typing.Optional[str],
        files: typing.List[cgi.FieldStorage],
        do_notify: bool = False,
    ) -> typing.List[ContentInContext]:
        content_api = ContentApi(
            config=self._config,
            current_user=upload_permission.author,
            session=self._session,
            namespaces_filter=[ContentNamespaces.UPLOAD],
        )
        translator = Translator(app_config=self._config)
        _ = translator.get_translation
        current_datetime = datetime.utcnow()
        folder_label = _(
            "Files uploaded by {username} on {date} at {time}").format(
                username=uploader_username,
                date=format_date(current_datetime,
                                 locale=translator.default_lang),
                time=format_time(current_datetime,
                                 locale=translator.default_lang),
            )

        try:
            upload_folder = content_api.create(
                content_type_slug=content_type_list.Folder.slug,
                workspace=upload_permission.workspace,
                label=folder_label,
                do_notify=False,
                do_save=True,
                content_namespace=ContentNamespaces.UPLOAD,
            )
        except ContentFilenameAlreadyUsedInFolder:
            upload_folder = content_api.get_one_by_filename(
                filename=folder_label, workspace=upload_permission.workspace)

        created_contents = []
        if message:
            comment_message = _("Message from {username}: {message}").format(
                username=uploader_username, message=message)
        else:
            comment_message = _("Uploaded by {username}.").format(
                username=uploader_username)

        for _file in files:
            content = content_api.create(
                filename=_file.filename,
                content_type_slug=content_type_list.File.slug,
                workspace=upload_permission.workspace,
                parent=upload_folder,
                do_notify=False,
                content_namespace=ContentNamespaces.UPLOAD,
            )
            content_api.save(content, ActionDescription.CREATION)
            with new_revision(session=self._session,
                              tm=transaction.manager,
                              content=content):
                content_api.update_file_data(
                    content,
                    new_filename=_file.filename,
                    new_mimetype=_file.type,
                    new_content=_file.file,
                )
            content_api.create_comment(parent=content,
                                       content=comment_message,
                                       do_save=True,
                                       do_notify=False)
            created_contents.append(
                content_api.get_content_in_context(content))
            content_api.execute_created_content_actions(content)

        if do_notify:
            workspace_lib = WorkspaceApi(config=self._config,
                                         current_user=upload_permission.author,
                                         session=self._session)
            self._notify_uploaded_contents(
                uploader_username=uploader_username,
                workspace_in_context=workspace_lib.get_workspace_with_context(
                    upload_permission.workspace),
                uploader_message=message,
                uploaded_contents=created_contents,
                uploader_email=upload_permission.email,
            )

        return created_contents
Exemplo n.º 16
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
        )
Exemplo n.º 17
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
        )
Exemplo n.º 18
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)
Exemplo n.º 19
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
            )
Exemplo n.º 20
0
    def _build_context_for_content_update(
            self,
            role: UserRoleInWorkspace,
            content_in_context: ContentInContext,
            parent_in_context: typing.Optional[ContentInContext],
            workspace_in_context: WorkspaceInContext,
            actor: User,
            translator: Translator
    ):

        _ = translator.get_translation
        content = content_in_context.content
        action = content.get_last_action().id

        # default values
        user = role.user
        workspace = role.workspace
        workspace_url = workspace_in_context.frontend_url
        main_title = content.label
        status_label = content.get_status().label
        # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for status_icon_url  # nopep8
        status_icon_url = ''
        role_label = role.role_as_label()
        content_intro = '<span id="content-intro-username">{}</span> did something.'.format(actor.display_name)  # nopep8
        content_text = content.description
        call_to_action_url = content_in_context.frontend_url
        logo_url = get_email_logo_frontend_url(self.config)

        if ActionDescription.COMMENT == action:
            main_title = parent_in_context.label
            content_intro = ''
            call_to_action_url = parent_in_context.frontend_url
        elif ActionDescription.STATUS_UPDATE == action:
            new_status = translator.get_translation(content.get_status().label)
            main_title = content_in_context.label
            content_intro = _('I modified the status of <i>{content}</i>. The new status is <i>{new_status}</i>').format(
                content=content.get_label(),
                new_status=new_status
            )
            content_text = ''
            call_to_action_url = content_in_context.frontend_url
        elif ActionDescription.CREATION == action:
            main_title = content_in_context.label
            content_intro = _('I added an item entitled <i>{content}</i>.').format(content=content.get_label())  # nopep8
            content_text = ''
        elif action in (ActionDescription.REVISION, ActionDescription.EDITION):
            main_title = content_in_context.label
            content_intro = _('I updated <i>{content}</i>.').format(content=content.get_label())  # nopep8

            previous_revision = content.get_previous_revision()
            title_diff = htmldiff(previous_revision.label, content.label)
            content_diff = htmldiff(previous_revision.description, content.description)
            if title_diff or content_diff:
                content_text = str('<p>{diff_intro_text}</p>\n{title_diff}\n{content_diff}').format(  # nopep8
                    diff_intro_text=_('Here is an overview of the changes:'),
                    title_diff=title_diff,
                    content_diff=content_diff
                )
        # if not content_intro and not content_text:
        #     # Skip notification, but it's not normal
        #     logger.error(
        #         self,
        #         'A notification is being sent but no content. '
        #         'Here are some debug informations: [content_id: {cid}]'
        #         '[action: {act}][author: {actor}]'.format(
        #             cid=content.content_id,
        #             act=action,
        #             actor=actor
        #         )
        #     )
        #     raise EmptyNotificationError('Unexpected empty notification')

        # FIXME: remove/readapt assert to debug easily broken case
        assert user
        assert workspace
        assert main_title
        assert status_label
        # assert status_icon_url
        assert role_label
        # assert content_intro
        assert content_text or content_text == content.description
        assert call_to_action_url
        assert logo_url

        return {
            'user': role.user,
            'workspace': role.workspace,
            'workspace_url': workspace_url,
            'main_title': main_title,
            'status_label': status_label,
            'status_icon_url': status_icon_url,
            'role_label': role_label,
            'content_intro': content_intro,
            'content_text': content_text,
            'call_to_action_url': call_to_action_url,
            'logo_url': logo_url,
        }