def doom_request_transaction(self, exc: Exception, *args, **kwargs) -> None: try: # NOTE 2020-09-09 - S.G. # we have to search for the request object in all arguments # as we cannot be sure of its place in it. # For example if a handler is an object method the first argument # will be the controller object, not the request object. request = next(arg for arg in args if isinstance(arg, Request)) transaction_status = request.tm.get().status # INFO - 2020-10-01 - G.M : In some specific case: we do arrive in situation # where transaction is not active anymore, in such situation, it's not possible # to doom the request and we do get " ValueError('non-doomable')" Exception. # to handle this case in a more clear way, we do Doom only active Transaction. if transaction_status == TransactionStatus.ACTIVE: request.tm.doom() else: # INFO - 2020-10-01 - G.M : debug unattended transaction status # for troubleshooting future issues logger.debug( self, 'Transaction not active, status : "{}", cannot be doomed'.format( transaction_status ), ) except StopIteration: logger.error(self, "Cannot find request object in arguments") except NoTransaction: # INFO - 2020-10-01 - G.M - In some very specific case like PageNotFound case, pyramid # doesn't provide a true transaction, so doom it will raise a NoTransaction exception. logger.debug(self, "Transaction not initialized, cannot be doomed")
def execute_updated_user_actions(self, user: User) -> None: """ WARNING! This method will be deprecated soon, see https://github.com/tracim/tracim/issues/1589 and https://github.com/tracim/tracim/issues/1487 This method does post-update user actions """ # TODO - G.M - 04-04-2018 - [auth] # Check if this is already needed with # new auth system user.ensure_auth_token( validity_seconds=self._config.USER__AUTH_TOKEN__VALIDITY) # FIXME - G.M - 2019-03-18 - move this code to another place when # event mechanism is ready, see https://github.com/tracim/tracim/issues/1487 # event on_updated_user should start hook use by agenda app code. app_lib = ApplicationApi(app_list=app_list) if app_lib.exist(AGENDA__APP_SLUG): agenda_api = AgendaApi(current_user=self._user, session=self._session, config=self._config) try: agenda_api.ensure_user_agenda_exists(user) except AgendaServerConnectionError as exc: logger.error(self, "Cannot connect to agenda server") logger.exception(self, exc) except Exception as exc: logger.error( self, "Something goes wrong during agenda create/update") logger.exception(self, exc)
def execute_created_user_actions(self, user: User) -> None: """ WARNING! This method will be deprecated soon, see https://github.com/tracim/tracim/issues/1589 and https://github.com/tracim/tracim/issues/1487 This method do post-create user actions """ # FIXME - G.M - 2019-03-18 - move this code to another place when # event mechanism is ready, see https://github.com/tracim/tracim/issues/1487 # event on_created_user should start hook use by agenda app code. app_lib = ApplicationApi(app_list=app_list) if app_lib.exist(AGENDA__APP_SLUG): agenda_api = AgendaApi(current_user=self._user, session=self._session, config=self._config) try: agenda_already_exist = agenda_api.ensure_user_agenda_exists( user) if agenda_already_exist: logger.warning( self, "user {} has just been created but their own agenda already exists" .format(user.user_id), ) except AgendaServerConnectionError as exc: logger.error(self, "Cannot connect to the agenda server") logger.exception(self, exc) except Exception as exc: logger.error( self, "Something went wrong during agenda create/update") logger.exception(self, exc)
def execute_updated_user_actions(self, user: User) -> None: """ WARNING ! This method Will be Deprecated soon, see https://github.com/tracim/tracim/issues/1589 and https://github.com/tracim/tracim/issues/1487 This method do post-update user actions """ # FIXME - G.M - 2019-03-18 - move this code to another place when # event mecanism is ready, see https://github.com/tracim/tracim/issues/1487 # event on_updated_user should start hook use by agenda app code. if self._config.CALDAV__ENABLED: agenda_api = AgendaApi(current_user=self._user, session=self._session, config=self._config) try: agenda_api.ensure_user_agenda_exists(user) except AgendaServerConnectionError as exc: logger.error(self, "Cannot connect to agenda server") logger.exception(self, exc) except Exception as exc: logger.error( self, "Something goes wrong during agenda create/update") logger.exception(self, exc)
def execute_update_workspace_actions(self, workspace: Workspace) -> None: """ WARNING ! This method Will be Deprecated soon, see https://github.com/tracim/tracim/issues/1589 and https://github.com/tracim/tracim/issues/1487 This method do post update workspace actions """ # FIXME - G.M - 2019-03-18 - move this code to another place when # event mechanism is ready, see https://github.com/tracim/tracim/issues/1487 # event on_updated_workspace should start hook use by agenda app code. app_lib = ApplicationApi(app_list=app_list) if app_lib.exist(AGENDA__APP_SLUG): # TODO - G.M - 2019-04-11 - Circular Import, will probably be remove # with event refactor, see https://github.com/tracim/tracim/issues/1487 from tracim_backend.applications.agenda.lib import AgendaApi if workspace.agenda_enabled: agenda_api = AgendaApi(current_user=self._user, session=self._session, config=self._config) try: agenda_api.ensure_workspace_agenda_exists(workspace) except AgendaServerConnectionError as exc: logger.error(self, "Cannot connect to agenda server") logger.exception(self, exc) except Exception as exc: logger.error( self, "Something goes wrong during agenda create/update") logger.exception(self, exc)
def get_content_schema_for_type(cls, content_type: str) -> ContentSchema: try: return cls.content_schemas[content_type] except KeyError: logger.error( cls, ("Cannot dump proper content for content-type '{}' in generated event " "as it is unknown, falling back to generic content schema" ).format(content_type), ) return ContentSchema()
def connect(self): if not self._smtp_connection: log = "Connecting to SMTP server {}" logger.info(self, log.format(self._smtp_config.server)) if self._smtp_config.use_implicit_ssl: self._smtp_connection = smtplib.SMTP_SSL( self._smtp_config.server, self._smtp_config.port ) else: self._smtp_connection = smtplib.SMTP( self._smtp_config.server, self._smtp_config.port ) self._smtp_connection.ehlo() # TODO - G.M - 2020-04-02 - Starttls usage should be explicit in configuration, see # https://github.com/tracim/tracim/issues/2815 if self._smtp_config.login and not self._smtp_config.use_implicit_ssl: try: starttls_result = self._smtp_connection.starttls() if starttls_result[0] == 220: logger.info(self, "SMTP Start TLS OK") log = "SMTP Start TLS return code: {} with message: {}" logger.debug( self, log.format(starttls_result[0], starttls_result[1].decode("utf-8")) ) except smtplib.SMTPResponseException as exc: log = "SMTP start TLS return error code: {} with message: {}" logger.error(self, log.format(exc.smtp_code, exc.smtp_error.decode("utf-8"))) except Exception: log = "Unexpected exception during SMTP start TLS process" logger.exception(self, log) if self._smtp_config.login: try: login_res = self._smtp_connection.login( self._smtp_config.login, self._smtp_config.password ) if login_res[0] == 235: logger.info(self, "SMTP Authentication Successful") if login_res[0] == 503: logger.info(self, "SMTP Already Authenticated") log = "SMTP login return code: {} with message: {}" logger.debug(self, log.format(login_res[0], login_res[1].decode("utf-8"))) except smtplib.SMTPAuthenticationError as exc: log = "SMTP auth return error code: {} with message: {}" logger.error(self, log.format(exc.smtp_code, exc.smtp_error.decode("utf-8"))) logger.error( self, "check your auth params combinaison " "(login/password) for SMTP" ) except smtplib.SMTPResponseException as exc: log = "SMTP login return error code: {} with message: {}" logger.error(self, log.format(exc.smtp_code, exc.smtp_error.decode("utf-8"))) except Exception: log = "Unexpected exception during SMTP login" logger.exception(self, log)
def _notify_tracim(self, mails: typing.List[DecodedMail], imapc: imapclient.IMAPClient) -> None: """ Send http request to tracim endpoint :param mails: list of mails to send :return: none """ logger.debug(self, "Notify tracim about {} new responses".format(len(mails))) # TODO BS 20171124: Look around mail.get_from_address(), mail.get_key() # , mail.get_body() etc ... for raise InvalidEmailError if missing # required informations (actually get_from_address raise IndexError # if no from address for example) and catch it here while mails: mail = mails.pop() try: method, endpoint, json_body_dict = self._create_comment_request( mail) except NoKeyFound: log = "Failed to create comment request due to missing specialkey in mail" logger.exception(self, log) continue except EmptyEmailBody: log = "Empty body, skip mail" logger.error(self, log) continue except AutoReplyEmailNotAllowed: log = "Autoreply mail, skip mail" logger.warning(self, log) continue except Exception: log = "Failed to create comment request in mail fetcher error" logger.exception(self, log) continue try: self._send_request( mail=mail, imapc=imapc, method=method, endpoint=endpoint, json_body_dict=json_body_dict, ) except requests.exceptions.Timeout: log = "Timeout error to transmit fetched mail to tracim" logger.exception(self, log) except requests.exceptions.RequestException: log = "Fail to transmit fetched mail to tracim" logger.exception(self, log)
def _check_agenda_exist(self, agenda_url) -> bool: try: response = requests.get(agenda_url) except requests.exceptions.ConnectionError as exc: logger.error( self, "Cannot check agenda existence, connection error to radicale server" ) logger.exception(self, exc) raise AgendaServerConnectionError() from exc if response.status_code < 400: return True else: # TODO - G.M - 2019-03-13 - Better deal with other error code return False
def _notify_tracim(self, mails: typing.List[DecodedMail], imapc: imapclient.IMAPClient) -> None: """ Send http request to tracim endpoint :param mails: list of mails to send :return: none """ logger.debug( self, 'Notify tracim about {} new responses'.format(len(mails), )) # TODO BS 20171124: Look around mail.get_from_address(), mail.get_key() # , mail.get_body() etc ... for raise InvalidEmailError if missing # required informations (actually get_from_address raise IndexError # if no from address for example) and catch it here while mails: mail = mails.pop() try: method, endpoint, json_body_dict = self._create_comment_request( mail) # nopep8 except NoSpecialKeyFound as exc: log = 'Failed to create comment request due to missing specialkey in mail {}' # nopep8 logger.error(self, log.format(exc.__str__())) continue except EmptyEmailBody as exc: log = 'Empty body, skip mail' logger.error(self, log) continue except Exception as exc: log = 'Failed to create comment request in mail fetcher error {}' # nopep8 logger.error(self, log.format(exc.__str__())) continue try: self._send_request( mail=mail, imapc=imapc, method=method, endpoint=endpoint, json_body_dict=json_body_dict, ) except requests.exceptions.Timeout as e: log = 'Timeout error to transmit fetched mail to tracim : {}' logger.error(self, log.format(str(e))) except requests.exceptions.RequestException as e: log = 'Fail to transmit fetched mail to tracim : {}' logger.error(self, log.format(str(e)))
def index_all_content(self) -> IndexedContentsResults: """ Index/update all content in current index of ElasticSearch """ content_api = ContentApi( session=self._session, config=self._config, current_user=self._user, show_archived=True, show_active=True, show_deleted=True, ) contents = content_api.get_all() content_ids_to_index = [] # type: typing.List[int] errored_indexed_contents_ids = [] # type: typing.List[int] for content in contents: content_in_context = ContentInContext( content, config=self._config, dbsession=self._session ) content_ids_to_index.append(content_in_context.content_id) try: self.index_content(content_in_context) except ConnectionError as exc: logger.error( self, "connexion error issue with elasticsearch during indexing of content {}".format( content_in_context.content_id ), ) logger.exception(self, exc) errored_indexed_contents_ids.append(content_in_context.content_id) except Exception as exc: logger.error( self, "something goes wrong during indexing of content {}".format( content_in_context.content_id ), ) logger.exception(self, exc) errored_indexed_contents_ids.append(content_in_context.content_id) return IndexedContentsResults(content_ids_to_index, errored_indexed_contents_ids)
def execute_created_workspace_actions(self, workspace: Workspace) -> None: """ WARNING ! This method Will be Deprecated soon, see https://github.com/tracim/tracim/issues/1589 and https://github.com/tracim/tracim/issues/1487 This method do post creation workspace actions """ # FIXME - G.M - 2019-03-18 - move this code to another place when # event mecanism is ready, see https://github.com/tracim/tracim/issues/1487 # event on_created_workspace should start hook use by agenda app code. # TODO - G.M - 2019-04-11 - Circular Import, will probably be remove # with event refactor, see https://github.com/tracim/tracim/issues/1487 from tracim_backend.lib.agenda.agenda import AgendaApi if self._config.CALDAV__ENABLED: if workspace.agenda_enabled: agenda_api = AgendaApi(current_user=self._user, session=self._session, config=self._config) try: agenda_already_exist = agenda_api.ensure_workspace_agenda_exists( workspace) if agenda_already_exist: logger.warning( self, "workspace {} is just created but it own agenda already exist !!" .format(workspace.workspace_id), ) except AgendaServerConnectionError as exc: logger.error(self, "Cannot connect to agenda server") logger.exception(self, exc) except Exception as exc: logger.error( self, "Something goes wrong during agenda create/update") logger.exception(self, exc)
def execute_created_user_actions(self, user: User) -> None: """ WARNING ! This method Will be Deprecated soon, see https://github.com/tracim/tracim/issues/1589 and https://github.com/tracim/tracim/issues/1487 This method do post-create user actions """ # TODO - G.M - 04-04-2018 - [auth] # Check if this is already needed with # new auth system user.ensure_auth_token( validity_seconds=self._config.USER__AUTH_TOKEN__VALIDITY) # FIXME - G.M - 2019-03-18 - move this code to another place when # event mecanism is ready, see https://github.com/tracim/tracim/issues/1487 # event on_created_user should start hook use by agenda app code. if self._config.CALDAV__ENABLED: agenda_api = AgendaApi(current_user=self._user, session=self._session, config=self._config) try: agenda_already_exist = agenda_api.ensure_user_agenda_exists( user) if agenda_already_exist: logger.warning( self, "user {} is just created but his own agenda already exist !!" .format(user.user_id), ) except AgendaServerConnectionError as exc: logger.error(self, "Cannot connect to agenda server") logger.exception(self, exc) except Exception as exc: logger.error( self, "Something goes wrong during agenda create/update") logger.exception(self, exc)
def send_mail(self, message: MIMEMultipart): if not self._is_active: log = "Not sending email to {} (service disabled)" logger.info(self, log.format(message["To"])) else: self.connect() # Actually, this connects to SMTP only if required logger.info(self, "Sending email to {}".format(message["To"])) # TODO - G.M - 2019-01-29 - optimisize this code, we should not send # email if connection has failed. send_action = "{:8s}".format("SENT") failed_action = "{:8s}".format("SENDFAIL") action = send_action try: send_message_result = self._smtp_connection.send_message( message) # INFO - G.M - 2019-01-29 - send_message return if not failed, # dict of refused recipients. if send_message_result == {}: logger.debug(self, "One mail correctly sent using SMTP.") else: # INFO - G.M - 2019-01-29 - send_message_result != {} # case should not happened # as we send not mail with multiple recipient at the same # time. send_message will not raise exception # just if some recipient work and some other failed. # TODO - G.M - 2019-01-29 - better support for multirecipient email log = "Mail could not be send to some recipient: {}" logger.debug(self, log.format(send_message_result)) action = failed_action except smtplib.SMTPException as exc: log = "SMTP sending message return error: {}" logger.error(self, log.format(str(exc))) action = failed_action except Exception as exc: log = "Unexpected exception during sending email message using SMTP: {}" logger.error(self, log.format(exc.__str__())) logger.error(self, traceback.format_exc()) action = failed_action from tracim_backend.lib.mail_notifier.notifier import EmailManager if action == send_action: msg = "an email was sended to {}".format(message["To"]) else: msg = "fail to send email to {}".format(message["To"]) EmailManager.log_email_notification( msg=msg, action=action, email_recipient=message["To"], email_subject=message["Subject"], config=self.config, )
def send_mail(self, message: MIMEMultipart): if not self._is_active: log = 'Not sending email to {} (service disabled)' logger.info(self, log.format(message['To'])) else: self.connect() # Actually, this connects to SMTP only if required logger.info(self, 'Sending email to {}'.format(message['To'])) # TODO - G.M - 2019-01-29 - optimisize this code, we should not send # email if connection has failed. send_action = '{:8s}'.format('SENT') failed_action = '{:8s}'.format('SENDFAIL') action = send_action try: send_message_result = self._smtp_connection.send_message(message) # INFO - G.M - 2019-01-29 - send_message return if not failed, # dict of refused recipients. if send_message_result == {}: logger.debug(self, 'One mail correctly sent using SMTP.') else: # INFO - G.M - 2019-01-29 - send_message_result != {} # case should not happened # as we send not mail with multiple recipient at the same # time. send_message will not raise exception # just if some recipient work and some other failed. # TODO - G.M - 2019-01-29 - better support for multirecipient email log = 'Mail could not be send to some recipient: {}' logger.debug(self, log.format(send_message_result)) action = failed_action except smtplib.SMTPException as exc: log = 'SMTP sending message return error: {}' logger.error(self, log.format(str(exc))) action = failed_action except Exception as exc: log = 'Unexpected exception during sending email message using SMTP: {}' logger.error(self, log.format(exc.__str__())) logger.error(self, traceback.format_exc()) action = failed_action from tracim_backend.lib.mail_notifier.notifier import EmailManager if action == send_action: msg = 'an email was sended to {}'.format(message['To']) else: msg = 'fail to send email to {}'.format(message['To']) EmailManager.log_email_notification( msg=msg, action=action, email_recipient=message['To'], email_subject=message['Subject'], config=self.config, )
def init_database(self, settings: typing.Dict[str, typing.Any]): with transaction.manager: try: fixtures_loader = FixturesLoader(self.session, self.app_config) fixtures_loader.loads(self.fixtures) transaction.commit() logger.info(self, "Database initialized.") except IntegrityError: logger.error( self, 'Warning, there was a problem when adding default data' # nopep8 ', it may have already been added:') import traceback logger.error(self, traceback.format_exc()) transaction.abort() logger.error(self, 'Database initialization failed')
def init_database(self) -> None: session = get_tm_session(self.session_factory, transaction.manager) with transaction.manager: try: DeclarativeBase.metadata.create_all(self.engine) fixtures_loader = FixturesLoader(session, self.app_config) fixtures_loader.loads(self.fixtures) transaction.commit() logger.info(self, "Database initialized.") except IntegrityError: logger.error( self, 'Warning, there was a problem when adding default data' # nopep8 ', it may have already been added:') import traceback logger.error(self, traceback.format_exc()) transaction.abort() logger.error(self, 'Database initialization failed')
def run(self) -> None: logger.info(self, "Starting MailFetcher") while self._is_active: imapc = None sleep_after_connection = True try: imapc = imapclient.IMAPClient( self.host, self.port, ssl=self.use_ssl, timeout=MAIL_FETCHER_CONNECTION_TIMEOUT ) imapc.login(self.user, self.password) logger.debug(self, "Select folder {}".format(self.folder)) imapc.select_folder(self.folder) # force renew connection when deadline is reached deadline = time.time() + self.connection_max_lifetime while True: if not self._is_active: logger.warning(self, "Mail Fetcher process aborted") sleep_after_connection = False break if time.time() > deadline: logger.debug( self, "MailFetcher Connection Lifetime limit excess" ", Try Re-new connection", ) sleep_after_connection = False break # check for new mails self._check_mail(imapc) if self.use_idle and imapc.has_capability("IDLE"): # IDLE_mode wait until event from server logger.debug(self, "wail for event(IDLE)") imapc.idle() imapc.idle_check(timeout=MAIL_FETCHER_IDLE_RESPONSE_TIMEOUT) imapc.idle_done() else: if self.use_idle and not imapc.has_capability("IDLE"): log = ( "IDLE mode activated but server do not" "support it, use polling instead." ) logger.warning(self, log) if self.burst: self.stop() break # normal polling mode : sleep a define duration logger.debug(self, "sleep for {}".format(self.heartbeat)) time.sleep(self.heartbeat) if self.burst: self.stop() break # Socket except (socket.error, socket.gaierror, socket.herror) as e: log = "Socket fail with IMAP connection {}" logger.error(self, log.format(e.__str__())) except socket.timeout as e: log = "Socket timeout on IMAP connection {}" logger.error(self, log.format(e.__str__())) # SSL except ssl.SSLError as e: log = "SSL error on IMAP connection" logger.error(self, log.format(e.__str__())) except ssl.CertificateError as e: log = "SSL Certificate verification failed on IMAP connection" logger.error(self, log.format(e.__str__())) # Filelock except filelock.Timeout as e: log = "Mail Fetcher Lock Timeout {}" logger.warning(self, log.format(e.__str__())) # IMAP # TODO - G.M - 10-01-2017 - Support imapclient exceptions # when Imapclient stable will be 2.0+ except BadIMAPFetchResponse as e: log = ( "Imap Fetch command return bad response." "Is someone else connected to the mailbox ?: " "{}" ) logger.error(self, log.format(e.__str__())) # Others except Exception as e: log = "Mail Fetcher error {}" logger.error(self, log.format(e.__str__())) finally: # INFO - G.M - 2018-01-09 - Connection closing # Properly close connection according to # https://github.com/mjs/imapclient/pull/279/commits/043e4bd0c5c775c5a08cb5f1baa93876a46732ee # TODO : Use __exit__ method instead when imapclient stable will # be 2.0+ . if imapc: logger.debug(self, "Try logout") try: imapc.logout() except Exception: try: imapc.shutdown() except Exception as e: log = "Can't logout, connection broken ? {}" logger.error(self, log.format(e.__str__())) if self.burst: self.stop() break if sleep_after_connection: logger.debug(self, "sleep for {}".format(self.heartbeat)) time.sleep(self.heartbeat) log = "Mail Fetcher stopped" logger.debug(self, log)
def notify_content_update(self, content: Content): if content.get_last_action().id not \ in self.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 self.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 self.config.EMAIL_NOTIFICATION_PROCESSING_MODE.lower() == self.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') else: logger.info(self, 'Sending email in SYNC mode') EmailManager( self._smtp_config, self.config, self.session, ).notify_content_update(self._user.user_id, content.content_id) except Exception as e: # TODO - G.M - 2018-08-27 - Do Better catching for exception here logger.error(self, 'Exception catched during email notification: {}'.format(e.__str__())) logger.exception(self, e)
def connect(self): if not self._smtp_connection: log = 'Connecting to SMTP server {}' logger.info(self, log.format(self._smtp_config.server)) # TODO - G.M - 2019-01-29 - Support for SMTP SSL-only port connection # using smtplib.SMTP_SSL self._smtp_connection = smtplib.SMTP( self._smtp_config.server, self._smtp_config.port ) self._smtp_connection.ehlo() if self._smtp_config.login: try: starttls_result = self._smtp_connection.starttls() if starttls_result[0] == 220: logger.info(self, 'SMTP Start TLS OK') log = 'SMTP Start TLS return code: {} with message: {}' logger.debug( self, log.format( starttls_result[0], starttls_result[1].decode('utf-8') ) ) except smtplib.SMTPResponseException as exc: log = 'SMTP start TLS return error code: {} with message: {}' logger.error( self, log.format( exc.smtp_code, exc.smtp_error.decode('utf-8') ) ) except Exception as exc: log = 'Unexpected exception during SMTP start TLS process: {}' logger.error(self, log.format(exc.__str__())) logger.error(self, traceback.format_exc()) if self._smtp_config.login: try: login_res = self._smtp_connection.login( self._smtp_config.login, self._smtp_config.password ) if login_res[0] == 235: logger.info(self, 'SMTP Authentication Successful') if login_res[0] == 503: logger.info(self, 'SMTP Already Authenticated') log = 'SMTP login return code: {} with message: {}' logger.debug( self, log.format( login_res[0], login_res[1].decode('utf-8') ) ) except smtplib.SMTPAuthenticationError as exc: log = 'SMTP auth return error code: {} with message: {}' logger.error( self, log.format( exc.smtp_code, exc.smtp_error.decode('utf-8') ) ) logger.error(self, 'check your auth params combinaison ' '(login/password) for SMTP' ) except smtplib.SMTPResponseException as exc: log = 'SMTP login return error code: {} with message: {}' logger.error( self, log.format( exc.smtp_code, exc.smtp_error.decode('utf-8') ) ) except Exception as exc: log = 'Unexpected exception during SMTP login {}' logger.error(self, log.format(exc.__str__())) logger.error(self, traceback.format_exc())
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_text = 'See more' call_to_action_url = content_in_context.frontend_url logo_url = get_email_logo_frontend_url(self.config) if ActionDescription.CREATION == action: call_to_action_text = _('View online') content_intro = _('<span id="content-intro-username">{}</span> create a content:').format(actor.display_name) # nopep8 if content_type_list.Thread.slug == content.type: if content.get_last_comment_from(actor): content_text = content.get_last_comment_from(actor).description # nopep8 call_to_action_text = _('Answer') content_intro = _('<span id="content-intro-username">{}</span> started a thread entitled:').format(actor.display_name) content_text = '<p id="content-body-intro">{}</p>'.format(content.label) + content_text # nopep8 elif content_type_list.File.slug == content.type: content_intro = _('<span id="content-intro-username">{}</span> added a file entitled:').format(actor.display_name) if content.description: content_text = content.description else: content_text = '<span id="content-body-only-title">{}</span>'.format(content.label) elif content_type_list.Page.slug == content.type: content_intro = _('<span id="content-intro-username">{}</span> added a page entitled:').format(actor.display_name) content_text = '<span id="content-body-only-title">{}</span>'.format(content.label) elif ActionDescription.REVISION == action: content_text = content.description call_to_action_text = _('View online') if content_type_list.File.slug == content.type: content_intro = _('<span id="content-intro-username">{}</span> uploaded a new revision.').format(actor.display_name) content_text = content.description elif ActionDescription.EDITION == action: call_to_action_text = _('View online') if content_type_list.File.slug == content.type: content_intro = _('<span id="content-intro-username">{}</span> updated the file description.').format(actor.display_name) content_text = '<p id="content-body-intro">{}</p>'.format(content.get_label()) + content.description # nopep8 elif content_type_list.Thread.slug == content.type: content_intro = _('<span id="content-intro-username">{}</span> updated the thread description.').format(actor.display_name) previous_revision = content.get_previous_revision() title_diff = '' if previous_revision.label != content.label: title_diff = htmldiff(previous_revision.label, content.label) content_text = str('<p id="content-body-intro">{}</p> {text} {title_diff} {content_diff}').format( text=_('Here is an overview of the changes:'), title_diff=title_diff, content_diff=htmldiff(previous_revision.description, content.description) ) elif content_type_list.Page.slug == content.type: content_intro = _('<span id="content-intro-username">{}</span> updated this page.').format(actor.display_name) previous_revision = content.get_previous_revision() title_diff = '' if previous_revision.label != content.label: title_diff = htmldiff(previous_revision.label, content.label) # nopep8 content_text = str('<p id="content-body-intro">{}</p> {text}</p> {title_diff} {content_diff}').format( # nopep8 actor.display_name, text=_('Here is an overview of the changes:'), title_diff=title_diff, content_diff=htmldiff(previous_revision.description, content.description) ) elif ActionDescription.STATUS_UPDATE == action: intro_user_msg = _( '<span id="content-intro-username">{}</span> ' 'updated the following status:' ) intro_body_msg = '<p id="content-body-intro">{}: {}</p>' call_to_action_text = _('View online') content_intro = intro_user_msg.format(actor.display_name) content_text = intro_body_msg.format( content.get_label(), content.get_status().label, ) elif ActionDescription.COMMENT == action: call_to_action_text = _('Answer') main_title = parent_in_context.label content_intro = _('<span id="content-intro-username">{}</span> added a comment:').format(actor.display_name) # nopep8 call_to_action_url = parent_in_context.frontend_url 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_text 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_text': call_to_action_text, 'call_to_action_url': call_to_action_url, 'logo_url': logo_url, }
def connect(self): if not self._smtp_connection: log = "Connecting to SMTP server {}" logger.info(self, log.format(self._smtp_config.server)) # TODO - G.M - 2019-01-29 - Support for SMTP SSL-only port connection # using smtplib.SMTP_SSL self._smtp_connection = smtplib.SMTP(self._smtp_config.server, self._smtp_config.port) self._smtp_connection.ehlo() if self._smtp_config.login: try: starttls_result = self._smtp_connection.starttls() if starttls_result[0] == 220: logger.info(self, "SMTP Start TLS OK") log = "SMTP Start TLS return code: {} with message: {}" logger.debug( self, log.format(starttls_result[0], starttls_result[1].decode("utf-8"))) except smtplib.SMTPResponseException as exc: log = "SMTP start TLS return error code: {} with message: {}" logger.error( self, log.format(exc.smtp_code, exc.smtp_error.decode("utf-8"))) except Exception as exc: log = "Unexpected exception during SMTP start TLS process: {}" logger.error(self, log.format(exc.__str__())) logger.error(self, traceback.format_exc()) if self._smtp_config.login: try: login_res = self._smtp_connection.login( self._smtp_config.login, self._smtp_config.password) if login_res[0] == 235: logger.info(self, "SMTP Authentication Successful") if login_res[0] == 503: logger.info(self, "SMTP Already Authenticated") log = "SMTP login return code: {} with message: {}" logger.debug( self, log.format(login_res[0], login_res[1].decode("utf-8"))) except smtplib.SMTPAuthenticationError as exc: log = "SMTP auth return error code: {} with message: {}" logger.error( self, log.format(exc.smtp_code, exc.smtp_error.decode("utf-8"))) logger.error( self, "check your auth params combinaison " "(login/password) for SMTP") except smtplib.SMTPResponseException as exc: log = "SMTP login return error code: {} with message: {}" logger.error( self, log.format(exc.smtp_code, exc.smtp_error.decode("utf-8"))) except Exception as exc: log = "Unexpected exception during SMTP login {}" logger.error(self, log.format(exc.__str__())) logger.error(self, traceback.format_exc())