|
@@ -2,8 +2,14 @@
|
2
|
2
|
|
3
|
3
|
from email.mime.multipart import MIMEMultipart
|
4
|
4
|
import smtplib
|
|
5
|
+from email.mime.text import MIMEText
|
|
6
|
+
|
|
7
|
+from mako.template import Template
|
|
8
|
+from tgext.asyncjob import asyncjob_perform
|
|
9
|
+from tg.i18n import ugettext as _
|
5
|
10
|
|
6
|
11
|
from tracim.lib.base import logger
|
|
12
|
+from tracim.model import User
|
7
|
13
|
|
8
|
14
|
|
9
|
15
|
class SmtpConfiguration(object):
|
|
@@ -59,3 +65,118 @@ class EmailSender(object):
|
59
|
65
|
self.connect() # Acutally, this connects to SMTP only if required
|
60
|
66
|
logger.info(self, 'Sending email to {}'.format(message['To']))
|
61
|
67
|
self._smtp_connection.send_message(message)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+class EmailManager(object):
|
|
71
|
+ def __init__(self, smtp_config: SmtpConfiguration, global_config):
|
|
72
|
+ self._smtp_config = smtp_config
|
|
73
|
+ self._global_config = global_config
|
|
74
|
+
|
|
75
|
+ def notify_created_account(
|
|
76
|
+ self,
|
|
77
|
+ user: User,
|
|
78
|
+ password: str,
|
|
79
|
+ ) -> None:
|
|
80
|
+ """
|
|
81
|
+ Send created account email to given user
|
|
82
|
+ :param password: choosed password
|
|
83
|
+ :param user: user to notify
|
|
84
|
+ """
|
|
85
|
+ # TODO BS 20160712: Cyclic import
|
|
86
|
+ from tracim.lib.notifications import EST
|
|
87
|
+
|
|
88
|
+ logger.debug(self, 'user: {}'.format(user.user_id))
|
|
89
|
+ logger.info(self, 'Sending asynchronous email to 1 user ({0})'.format(
|
|
90
|
+ user.email,
|
|
91
|
+ ))
|
|
92
|
+
|
|
93
|
+ async_email_sender = EmailSender(
|
|
94
|
+ self._smtp_config,
|
|
95
|
+ self._global_config.EMAIL_NOTIFICATION_ACTIVATED
|
|
96
|
+ )
|
|
97
|
+
|
|
98
|
+ subject = \
|
|
99
|
+ self._global_config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_SUBJECT \
|
|
100
|
+ .replace(
|
|
101
|
+ EST.WEBSITE_TITLE,
|
|
102
|
+ self._global_config.WEBSITE_TITLE.__str__()
|
|
103
|
+ )
|
|
104
|
+ message = MIMEMultipart('alternative')
|
|
105
|
+ message['Subject'] = subject
|
|
106
|
+ message['From'] = self._global_config.EMAIL_NOTIFICATION_FROM
|
|
107
|
+ message['To'] = user.email
|
|
108
|
+
|
|
109
|
+ text_template_file_path = self._global_config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_TEXT # nopep8
|
|
110
|
+ html_template_file_path = self._global_config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_HTML # nopep8
|
|
111
|
+
|
|
112
|
+ body_text = self._render(
|
|
113
|
+ mako_template_filepath=text_template_file_path,
|
|
114
|
+ context={
|
|
115
|
+ 'user': user,
|
|
116
|
+ 'password': password,
|
|
117
|
+ 'login_url': self._global_config.WEBSITE_BASE_URL,
|
|
118
|
+ }
|
|
119
|
+ )
|
|
120
|
+
|
|
121
|
+ body_html = self._render(
|
|
122
|
+ mako_template_filepath=html_template_file_path,
|
|
123
|
+ context={
|
|
124
|
+ 'user': user,
|
|
125
|
+ 'password': password,
|
|
126
|
+ 'login_url': self._global_config.WEBSITE_BASE_URL,
|
|
127
|
+ }
|
|
128
|
+ )
|
|
129
|
+
|
|
130
|
+ part1 = MIMEText(body_text, 'plain', 'utf-8')
|
|
131
|
+ part2 = MIMEText(body_html, 'html', 'utf-8')
|
|
132
|
+
|
|
133
|
+ # Attach parts into message container.
|
|
134
|
+ # According to RFC 2046, the last part of a multipart message,
|
|
135
|
+ # in this case the HTML message, is best and preferred.
|
|
136
|
+ message.attach(part1)
|
|
137
|
+ message.attach(part2)
|
|
138
|
+
|
|
139
|
+ asyncjob_perform(async_email_sender.send_mail, message)
|
|
140
|
+
|
|
141
|
+ # Note: The following action allow to close the SMTP connection.
|
|
142
|
+ # This will work only if the async jobs are done in the right order
|
|
143
|
+ asyncjob_perform(async_email_sender.disconnect)
|
|
144
|
+
|
|
145
|
+ def _render(self, mako_template_filepath: str, context: dict):
|
|
146
|
+ """
|
|
147
|
+ Render mako template with all needed current variables
|
|
148
|
+ :param mako_template_filepath: file path of mako template
|
|
149
|
+ :param context: dict with template context
|
|
150
|
+ :return: template rendered string
|
|
151
|
+ """
|
|
152
|
+ # TODO - D.A. - 2014-11-06 - move this
|
|
153
|
+ # Import is here for circular import problem
|
|
154
|
+ import tracim.lib.helpers as helpers
|
|
155
|
+ from tracim.config.app_cfg import CFG
|
|
156
|
+
|
|
157
|
+ template = Template(filename=mako_template_filepath)
|
|
158
|
+ return template.render(
|
|
159
|
+ base_url=self._global_config.WEBSITE_BASE_URL,
|
|
160
|
+ _=_,
|
|
161
|
+ h=helpers,
|
|
162
|
+ CFG=CFG.get_instance(),
|
|
163
|
+ **context
|
|
164
|
+ )
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+def get_email_manager():
|
|
168
|
+ """
|
|
169
|
+ :return: EmailManager instance
|
|
170
|
+ """
|
|
171
|
+ # TODO: Find a way to import properly without cyclic import
|
|
172
|
+ from tracim.config.app_cfg import CFG
|
|
173
|
+
|
|
174
|
+ global_config = CFG.get_instance()
|
|
175
|
+ smtp_config = SmtpConfiguration(
|
|
176
|
+ global_config.EMAIL_NOTIFICATION_SMTP_SERVER,
|
|
177
|
+ global_config.EMAIL_NOTIFICATION_SMTP_PORT,
|
|
178
|
+ global_config.EMAIL_NOTIFICATION_SMTP_USER,
|
|
179
|
+ global_config.EMAIL_NOTIFICATION_SMTP_PASSWORD
|
|
180
|
+ )
|
|
181
|
+
|
|
182
|
+ return EmailManager(global_config=global_config, smtp_config=smtp_config)
|