def _send_email(sender, recipients, subject, html, send_async=True): if not isinstance(recipients, list): recipients = list(recipients) if send_async and config['_mailtemplates'][ 'async_sender'] == 'tgext.celery': from mailtemplates.lib.celery_tasks import mailtemplates_async_send_email mailtemplates_async_send_email.delay(subject=subject, sender=sender, recipients=recipients, html=html) elif send_async and config['_mailtemplates'][ 'async_sender'] == 'tgext.asyncjob': from tgext.asyncjob import asyncjob_perform mailer = get_mailer(None) message = Message(subject=subject, sender=sender, recipients=recipients, html=html) asyncjob_perform(mailer.send_immediately, message=message) else: try: mailer = get_mailer(_get_request()) log.debug('using request mailer') except AttributeError: log.debug('using global mailer in not-async context') mailer = get_mailer(None) message = Message(subject=subject, sender=sender, recipients=recipients, html=html) mailer.send_immediately(message)
def notify_content_update(self, content: Content): # TODO: Find a way to import properly without cyclic import from tracim.config.app_cfg import CFG global_config = CFG.get_instance() if content.get_last_action().id not \ in global_config.EMAIL_NOTIFICATION_NOTIFIED_EVENTS: logger.info( self, 'Skip email notification for update of content {}' 'by user {} (the action is {})'.format( content.content_id, # below: 0 means "no user" self._user.user_id if self._user else 0, content.get_last_action().id ) ) return logger.info(self, 'About to email-notify update' 'of content {} by user {}'.format( content.content_id, # Below: 0 means "no user" self._user.user_id if self._user else 0 ) ) if content.type not \ in global_config.EMAIL_NOTIFICATION_NOTIFIED_CONTENTS: logger.info( self, 'Skip email notification for update of content {}' 'by user {} (the content type is {})'.format( content.type, # below: 0 means "no user" self._user.user_id if self._user else 0, content.get_last_action().id ) ) return logger.info(self, 'About to email-notify update' 'of content {} by user {}'.format( content.content_id, # Below: 0 means "no user" self._user.user_id if self._user else 0 ) ) #### # # INFO - D.A. - 2014-11-05 - Emails are sent through asynchronous jobs. # For that reason, we do not give SQLAlchemy objects but ids only # (SQLA objects are related to a given thread/session) # try: if global_config.EMAIL_NOTIFICATION_PROCESSING_MODE.lower()==global_config.CST.ASYNC.lower(): logger.info(self, 'Sending email in ASYNC mode') # TODO - D.A - 2014-11-06 # This feature must be implemented in order to be able to scale to large communities raise NotImplementedError('Sending emails through ASYNC mode is not working yet') asyncjob_perform(EmailNotifier(self._smtp_config, global_config).notify_content_update, self._user.user_id, content.content_id) else: logger.info(self, 'Sending email in SYNC mode') EmailNotifier(self._smtp_config, global_config).notify_content_update(self._user.user_id, content.content_id) except Exception as e: logger.error(self, 'Exception catched during email notification: {}'.format(e.__str__()))
def notify_content_update(self, event_actor_id: int, event_content_id: int): """ 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.lib.content import ContentApi user = UserApi(None).get_one(event_actor_id) logger.debug(self, 'Content: {}'.format(event_content_id)) content = ContentApi(user, show_archived=True, show_deleted=True).get_one(event_content_id, ContentType.Any) # TODO - use a system user instead of the user that has triggered the event main_content = content.parent if content.type==ContentType.Comment else content notifiable_roles = WorkspaceApi(user).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, 'Sending asynchronous emails to {} user(s)'.format(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 async_email_sender = EmailSender(self._smtp_config, self._global_config.EMAIL_NOTIFICATION_ACTIVATED) for role in notifiable_roles: logger.info(self, 'Sending email to {}'.format(role.user.email)) to_addr = '{name} <{email}>'.format(name=role.user.display_name, email=role.user.email) # # 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 # subject = self._global_config.EMAIL_NOTIFICATION_CONTENT_UPDATE_SUBJECT subject = subject.replace(EST.WEBSITE_TITLE, self._global_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, main_content.get_status().label.__str__()) message = MIMEMultipart('alternative') message['Subject'] = subject message['From'] = self._global_config.EMAIL_NOTIFICATION_FROM message['To'] = to_addr body_text = self._build_email_body(self._global_config.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_TEXT, role, content, user) body_html = self._build_email_body(self._global_config.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_HTML, role, content, user) 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) message_str = message.as_string() asyncjob_perform(async_email_sender.send_mail, message) # s.send_message(message) # Note: The following action allow to close the SMTP connection. # This will work only if the async jobs are done in the right order asyncjob_perform(async_email_sender.disconnect)
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 from tracim.lib.notifications import EST 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._smtp_config, self._global_config.EMAIL_NOTIFICATION_ACTIVATED ) subject = \ self._global_config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_SUBJECT \ .replace( EST.WEBSITE_TITLE, self._global_config.WEBSITE_TITLE.__str__() ) message = MIMEMultipart('alternative') message['Subject'] = subject message['From'] = self._global_config.EMAIL_NOTIFICATION_FROM message['To'] = user.email text_template_file_path = self._global_config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_TEXT # nopep8 html_template_file_path = self._global_config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_HTML # nopep8 body_text = self._render( mako_template_filepath=text_template_file_path, context={ 'user': user, 'password': password, 'login_url': self._global_config.WEBSITE_BASE_URL, } ) body_html = self._render( mako_template_filepath=html_template_file_path, context={ 'user': user, 'password': password, 'login_url': self._global_config.WEBSITE_BASE_URL, } ) 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) asyncjob_perform(async_email_sender.send_mail, message) # Note: The following action allow to close the SMTP connection. # This will work only if the async jobs are done in the right order asyncjob_perform(async_email_sender.disconnect)
def notify_content_update(self, content: Content): # TODO: Find a way to import properly without cyclic import from tracim.config.app_cfg import CFG global_config = CFG.get_instance() if content.get_last_action().id not \ in global_config.EMAIL_NOTIFICATION_NOTIFIED_EVENTS: logger.info( self, 'Skip email notification for update of content {}' 'by user {} (the action is {})'.format( content.content_id, # below: 0 means "no user" self._user.user_id if self._user else 0, content.get_last_action().id)) return logger.info( self, 'About to email-notify update' 'of content {} by user {}'.format( content.content_id, # Below: 0 means "no user" self._user.user_id if self._user else 0)) if content.type not \ in global_config.EMAIL_NOTIFICATION_NOTIFIED_CONTENTS: logger.info( self, 'Skip email notification for update of content {}' 'by user {} (the content type is {})'.format( content.type, # below: 0 means "no user" self._user.user_id if self._user else 0, content.get_last_action().id)) return logger.info( self, 'About to email-notify update' 'of content {} by user {}'.format( content.content_id, # Below: 0 means "no user" self._user.user_id if self._user else 0)) #### # # INFO - D.A. - 2014-11-05 - Emails are sent through asynchronous jobs. # For that reason, we do not give SQLAlchemy objects but ids only # (SQLA objects are related to a given thread/session) # try: if global_config.EMAIL_NOTIFICATION_PROCESSING_MODE.lower( ) == global_config.CST.ASYNC.lower(): logger.info(self, 'Sending email in ASYNC mode') # TODO - D.A - 2014-11-06 # This feature must be implemented in order to be able to scale to large communities raise NotImplementedError( 'Sending emails through ASYNC mode is not working yet') asyncjob_perform( EmailNotifier(self._smtp_config, global_config).notify_content_update, self._user.user_id, content.content_id) else: logger.info(self, 'Sending email in SYNC mode') EmailNotifier(self._smtp_config, global_config).notify_content_update( self._user.user_id, content.content_id) except Exception as e: logger.error( self, 'Exception catched during email notification: {}'.format( e.__str__()))
def notify_content_update(self, event_actor_id: int, event_content_id: int): """ 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.lib.content import ContentApi user = UserApi(None).get_one(event_actor_id) logger.debug(self, 'Content: {}'.format(event_content_id)) content = ContentApi( user, show_archived=True, show_deleted=True ).get_one( event_content_id, ContentType.Any ) # TODO - use a system user instead of the user that has triggered the event main_content = content.parent if content.type == ContentType.Comment else content notifiable_roles = WorkspaceApi(user).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, 'Sending asynchronous emails to {} user(s)'.format( 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 async_email_sender = EmailSender( self._smtp_config, self._global_config.EMAIL_NOTIFICATION_ACTIVATED) for role in notifiable_roles: logger.info(self, 'Sending email to {}'.format(role.user.email)) to_addr = '{name} <{email}>'.format(name=role.user.display_name, email=role.user.email) # # 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 # subject = self._global_config.EMAIL_NOTIFICATION_CONTENT_UPDATE_SUBJECT subject = subject.replace( EST.WEBSITE_TITLE, self._global_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__() if main_content.label else main_content.file_name.__str__()) subject = subject.replace( EST.CONTENT_STATUS_LABEL, main_content.get_status().label.__str__()) message = MIMEMultipart('alternative') message['Subject'] = subject message['From'] = self._global_config.EMAIL_NOTIFICATION_FROM message['To'] = to_addr body_text = self._build_email_body( self._global_config. EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_TEXT, role, content, user) body_html = self._build_email_body( self._global_config. EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_HTML, role, content, user) 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) message_str = message.as_string() asyncjob_perform(async_email_sender.send_mail, message) # s.send_message(message) # Note: The following action allow to close the SMTP connection. # This will work only if the async jobs are done in the right order asyncjob_perform(async_email_sender.disconnect)
return tg.request def _send_email(sender, recipients, subject, html, async=True): if not isinstance(recipients, list): recipients = list(recipients) if async and config['_mailtemplates']['async_sender'] == 'tgext.celery': from mailtemplates.lib.celery_tasks import mailtemplates_async_send_email mailtemplates_async_send_email.delay(subject=subject, sender=sender, recipients=recipients, html=html) elif async and config['_mailtemplates']['async_sender'] == 'tgext.asyncjob': from tgext.asyncjob import asyncjob_perform mailer = get_mailer(None) message = Message(subject=subject, sender=sender, recipients=recipients, html=html) asyncjob_perform(mailer.send_immediately, message=message) else: mailer = get_mailer(_get_request()) message = Message(subject=subject, sender=sender, recipients=recipients, html=html) mailer.send_immediately(message) def _get_variables_for_template(tmpl): global_vars = [] for elem in dir(tmpl): if elem.startswith('_kj_block') or elem == '__main__': global_vars += _get_variables_for_block(tmpl, elem) return global_vars def _get_globals_py2(func):
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 from tracim.lib.notifications import EST 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._smtp_config, self._global_config.EMAIL_NOTIFICATION_ACTIVATED) subject = \ self._global_config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_SUBJECT \ .replace( EST.WEBSITE_TITLE, self._global_config.WEBSITE_TITLE.__str__() ) message = MIMEMultipart('alternative') message['Subject'] = subject message['From'] = self._global_config.EMAIL_NOTIFICATION_FROM message['To'] = user.email text_template_file_path = self._global_config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_TEXT # nopep8 html_template_file_path = self._global_config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_HTML # nopep8 body_text = self._render( mako_template_filepath=text_template_file_path, context={ 'user': user, 'password': password, 'login_url': self._global_config.WEBSITE_BASE_URL, }) body_html = self._render( mako_template_filepath=html_template_file_path, context={ 'user': user, 'password': password, 'login_url': self._global_config.WEBSITE_BASE_URL, }) 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) asyncjob_perform(async_email_sender.send_mail, message) # Note: The following action allow to close the SMTP connection. # This will work only if the async jobs are done in the right order asyncjob_perform(async_email_sender.disconnect)