##// END OF EJS Templates
integrations: email, fix plural/singular case and no pushed branches case....
marcink -
r2421:3ca1f08e default
parent child Browse files
Show More
@@ -1,283 +1,292 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2017 RhodeCode GmbH
3 # Copyright (C) 2012-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 from __future__ import unicode_literals
21 from __future__ import unicode_literals
22 import deform
22 import deform
23 import logging
23 import logging
24 import colander
24 import colander
25
25
26 from mako.template import Template
26 from mako.template import Template
27
27
28 from rhodecode import events
28 from rhodecode import events
29 from rhodecode.translation import _
29 from rhodecode.translation import _
30 from rhodecode.lib.celerylib import run_task
30 from rhodecode.lib.celerylib import run_task
31 from rhodecode.lib.celerylib import tasks
31 from rhodecode.lib.celerylib import tasks
32 from rhodecode.integrations.types.base import IntegrationTypeBase
32 from rhodecode.integrations.types.base import IntegrationTypeBase
33
33
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37 repo_push_template_plaintext = Template('''
37 repo_push_template_plaintext = Template('''
38 Commits:
38 Commits:
39
39
40 % for commit in data['push']['commits']:
40 % for commit in data['push']['commits']:
41 ${commit['url']} by ${commit['author']} at ${commit['date']}
41 ${commit['url']} by ${commit['author']} at ${commit['date']}
42 ${commit['message']}
42 ${commit['message']}
43 ----
43 ----
44
44
45 % endfor
45 % endfor
46 ''')
46 ''')
47
47
48 ## TODO (marcink): think about putting this into a file, or use base.mako email template
48 ## TODO (marcink): think about putting this into a file, or use base.mako email template
49
49
50 repo_push_template_html = Template('''
50 repo_push_template_html = Template('''
51 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
51 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
52 <html xmlns="http://www.w3.org/1999/xhtml">
52 <html xmlns="http://www.w3.org/1999/xhtml">
53 <head>
53 <head>
54 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
54 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
55 <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
55 <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
56 <title>${subject}</title>
56 <title>${subject}</title>
57 <style type="text/css">
57 <style type="text/css">
58 /* Based on The MailChimp Reset INLINE: Yes. */
58 /* Based on The MailChimp Reset INLINE: Yes. */
59 #outlook a {padding:0;} /* Force Outlook to provide a "view in browser" menu link. */
59 #outlook a {padding:0;} /* Force Outlook to provide a "view in browser" menu link. */
60 body{width:100% !important; -webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; margin:0; padding:0;}
60 body{width:100% !important; -webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; margin:0; padding:0;}
61 /* Prevent Webkit and Windows Mobile platforms from changing default font sizes.*/
61 /* Prevent Webkit and Windows Mobile platforms from changing default font sizes.*/
62 .ExternalClass {width:100%;} /* Force Hotmail to display emails at full width */
62 .ExternalClass {width:100%;} /* Force Hotmail to display emails at full width */
63 .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;}
63 .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;}
64 /* Forces Hotmail to display normal line spacing. More on that: http://www.emailonacid.com/forum/viewthread/43/ */
64 /* Forces Hotmail to display normal line spacing. More on that: http://www.emailonacid.com/forum/viewthread/43/ */
65 #backgroundTable {margin:0; padding:0; line-height: 100% !important;}
65 #backgroundTable {margin:0; padding:0; line-height: 100% !important;}
66 /* End reset */
66 /* End reset */
67
67
68 /* defaults for images*/
68 /* defaults for images*/
69 img {outline:none; text-decoration:none; -ms-interpolation-mode: bicubic;}
69 img {outline:none; text-decoration:none; -ms-interpolation-mode: bicubic;}
70 a img {border:none;}
70 a img {border:none;}
71 .image_fix {display:block;}
71 .image_fix {display:block;}
72
72
73 body {line-height:1.2em;}
73 body {line-height:1.2em;}
74 p {margin: 0 0 20px;}
74 p {margin: 0 0 20px;}
75 h1, h2, h3, h4, h5, h6 {color:#323232!important;}
75 h1, h2, h3, h4, h5, h6 {color:#323232!important;}
76 a {color:#427cc9;text-decoration:none;outline:none;cursor:pointer;}
76 a {color:#427cc9;text-decoration:none;outline:none;cursor:pointer;}
77 a:focus {outline:none;}
77 a:focus {outline:none;}
78 a:hover {color: #305b91;}
78 a:hover {color: #305b91;}
79 h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {color:#427cc9!important;text-decoration:none!important;}
79 h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {color:#427cc9!important;text-decoration:none!important;}
80 h1 a:active, h2 a:active, h3 a:active, h4 a:active, h5 a:active, h6 a:active {color: #305b91!important;}
80 h1 a:active, h2 a:active, h3 a:active, h4 a:active, h5 a:active, h6 a:active {color: #305b91!important;}
81 h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited {color: #305b91!important;}
81 h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited {color: #305b91!important;}
82 table {font-size:13px;border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt;}
82 table {font-size:13px;border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt;}
83 table td {padding:.65em 1em .65em 0;border-collapse:collapse;vertical-align:top;text-align:left;}
83 table td {padding:.65em 1em .65em 0;border-collapse:collapse;vertical-align:top;text-align:left;}
84 input {display:inline;border-radius:2px;border-style:solid;border: 1px solid #dbd9da;padding:.5em;}
84 input {display:inline;border-radius:2px;border-style:solid;border: 1px solid #dbd9da;padding:.5em;}
85 input:focus {outline: 1px solid #979797}
85 input:focus {outline: 1px solid #979797}
86 @media only screen and (-webkit-min-device-pixel-ratio: 2) {
86 @media only screen and (-webkit-min-device-pixel-ratio: 2) {
87 /* Put your iPhone 4g styles in here */
87 /* Put your iPhone 4g styles in here */
88 }
88 }
89
89
90 /* Android targeting */
90 /* Android targeting */
91 @media only screen and (-webkit-device-pixel-ratio:.75){
91 @media only screen and (-webkit-device-pixel-ratio:.75){
92 /* Put CSS for low density (ldpi) Android layouts in here */
92 /* Put CSS for low density (ldpi) Android layouts in here */
93 }
93 }
94 @media only screen and (-webkit-device-pixel-ratio:1){
94 @media only screen and (-webkit-device-pixel-ratio:1){
95 /* Put CSS for medium density (mdpi) Android layouts in here */
95 /* Put CSS for medium density (mdpi) Android layouts in here */
96 }
96 }
97 @media only screen and (-webkit-device-pixel-ratio:1.5){
97 @media only screen and (-webkit-device-pixel-ratio:1.5){
98 /* Put CSS for high density (hdpi) Android layouts in here */
98 /* Put CSS for high density (hdpi) Android layouts in here */
99 }
99 }
100 /* end Android targeting */
100 /* end Android targeting */
101
101
102 </style>
102 </style>
103
103
104 <!-- Targeting Windows Mobile -->
104 <!-- Targeting Windows Mobile -->
105 <!--[if IEMobile 7]>
105 <!--[if IEMobile 7]>
106 <style type="text/css">
106 <style type="text/css">
107
107
108 </style>
108 </style>
109 <![endif]-->
109 <![endif]-->
110
110
111 <!--[if gte mso 9]>
111 <!--[if gte mso 9]>
112 <style>
112 <style>
113 /* Target Outlook 2007 and 2010 */
113 /* Target Outlook 2007 and 2010 */
114 </style>
114 </style>
115 <![endif]-->
115 <![endif]-->
116 </head>
116 </head>
117 <body>
117 <body>
118 <!-- Wrapper/Container Table: Use a wrapper table to control the width and the background color consistently of your email. Use this approach instead of setting attributes on the body tag. -->
118 <!-- Wrapper/Container Table: Use a wrapper table to control the width and the background color consistently of your email. Use this approach instead of setting attributes on the body tag. -->
119 <table cellpadding="0" cellspacing="0" border="0" id="backgroundTable" align="left" style="margin:1%;width:97%;padding:0;font-family:sans-serif;font-weight:100;border:1px solid #dbd9da">
119 <table cellpadding="0" cellspacing="0" border="0" id="backgroundTable" align="left" style="margin:1%;width:97%;padding:0;font-family:sans-serif;font-weight:100;border:1px solid #dbd9da">
120 <tr>
120 <tr>
121 <td valign="top" style="padding:0;">
121 <td valign="top" style="padding:0;">
122 <table cellpadding="0" cellspacing="0" border="0" align="left" width="100%">
122 <table cellpadding="0" cellspacing="0" border="0" align="left" width="100%">
123 <tr><td style="width:100%;padding:7px;background-color:#202020" valign="top">
123 <tr><td style="width:100%;padding:7px;background-color:#202020" valign="top">
124 <a style="color:#eeeeee;text-decoration:none;" href="${instance_url}">
124 <a style="color:#eeeeee;text-decoration:none;" href="${instance_url}">
125 ${'RhodeCode'}
125 ${'RhodeCode'}
126 </a>
126 </a>
127 </td></tr>
127 </td></tr>
128 <tr>
128 <tr>
129 <td style="padding:15px;" valign="top">
129 <td style="padding:15px;" valign="top">
130 % for commit in data['push']['commits']:
130 % for commit in data['push']['commits']:
131 <a href="${commit['url']}">${commit['short_id']}</a> by ${commit['author']} at ${commit['date']} <br/>
131 <a href="${commit['url']}">${commit['short_id']}</a> by ${commit['author']} at ${commit['date']} <br/>
132 ${commit['message_html']} <br/>
132 ${commit['message_html']} <br/>
133 <br/>
133 <br/>
134 % endfor
134 % endfor
135 </td>
135 </td>
136 </tr>
136 </tr>
137 </table>
137 </table>
138 </td>
138 </td>
139 </tr>
139 </tr>
140 </table>
140 </table>
141 <!-- End of wrapper table -->
141 <!-- End of wrapper table -->
142 <p><a style="margin-top:15px;margin-left:1%;font-family:sans-serif;font-weight:100;font-size:11px;color:#666666;text-decoration:none;" href="${instance_url}">
142 <p><a style="margin-top:15px;margin-left:1%;font-family:sans-serif;font-weight:100;font-size:11px;color:#666666;text-decoration:none;" href="${instance_url}">
143 ${'This is a notification from RhodeCode. %(instance_url)s' % {'instance_url': instance_url}}
143 ${'This is a notification from RhodeCode. %(instance_url)s' % {'instance_url': instance_url}}
144 </a></p>
144 </a></p>
145 </body>
145 </body>
146 </html>
146 </html>
147 ''')
147 ''')
148
148
149 email_icon = '''
149 email_icon = '''
150 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
150 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
151 <svg
151 <svg
152 xmlns:dc="http://purl.org/dc/elements/1.1/"
152 xmlns:dc="http://purl.org/dc/elements/1.1/"
153 xmlns:cc="http://creativecommons.org/ns#"
153 xmlns:cc="http://creativecommons.org/ns#"
154 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
154 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
155 xmlns:svg="http://www.w3.org/2000/svg"
155 xmlns:svg="http://www.w3.org/2000/svg"
156 xmlns="http://www.w3.org/2000/svg"
156 xmlns="http://www.w3.org/2000/svg"
157 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
157 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
158 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
158 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
159 viewBox="0 -256 1850 1850"
159 viewBox="0 -256 1850 1850"
160 id="svg2989"
160 id="svg2989"
161 version="1.1"
161 version="1.1"
162 inkscape:version="0.48.3.1 r9886"
162 inkscape:version="0.48.3.1 r9886"
163 width="100%"
163 width="100%"
164 height="100%"
164 height="100%"
165 sodipodi:docname="envelope_font_awesome.svg">
165 sodipodi:docname="envelope_font_awesome.svg">
166 <metadata
166 <metadata
167 id="metadata2999">
167 id="metadata2999">
168 <rdf:RDF>
168 <rdf:RDF>
169 <cc:Work
169 <cc:Work
170 rdf:about="">
170 rdf:about="">
171 <dc:format>image/svg+xml</dc:format>
171 <dc:format>image/svg+xml</dc:format>
172 <dc:type
172 <dc:type
173 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
173 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
174 </cc:Work>
174 </cc:Work>
175 </rdf:RDF>
175 </rdf:RDF>
176 </metadata>
176 </metadata>
177 <defs
177 <defs
178 id="defs2997" />
178 id="defs2997" />
179 <sodipodi:namedview
179 <sodipodi:namedview
180 pagecolor="#ffffff"
180 pagecolor="#ffffff"
181 bordercolor="#666666"
181 bordercolor="#666666"
182 borderopacity="1"
182 borderopacity="1"
183 objecttolerance="10"
183 objecttolerance="10"
184 gridtolerance="10"
184 gridtolerance="10"
185 guidetolerance="10"
185 guidetolerance="10"
186 inkscape:pageopacity="0"
186 inkscape:pageopacity="0"
187 inkscape:pageshadow="2"
187 inkscape:pageshadow="2"
188 inkscape:window-width="640"
188 inkscape:window-width="640"
189 inkscape:window-height="480"
189 inkscape:window-height="480"
190 id="namedview2995"
190 id="namedview2995"
191 showgrid="false"
191 showgrid="false"
192 inkscape:zoom="0.13169643"
192 inkscape:zoom="0.13169643"
193 inkscape:cx="896"
193 inkscape:cx="896"
194 inkscape:cy="896"
194 inkscape:cy="896"
195 inkscape:window-x="0"
195 inkscape:window-x="0"
196 inkscape:window-y="25"
196 inkscape:window-y="25"
197 inkscape:window-maximized="0"
197 inkscape:window-maximized="0"
198 inkscape:current-layer="svg2989" />
198 inkscape:current-layer="svg2989" />
199 <g
199 <g
200 transform="matrix(1,0,0,-1,37.966102,1282.678)"
200 transform="matrix(1,0,0,-1,37.966102,1282.678)"
201 id="g2991">
201 id="g2991">
202 <path
202 <path
203 d="m 1664,32 v 768 q -32,-36 -69,-66 -268,-206 -426,-338 -51,-43 -83,-67 -32,-24 -86.5,-48.5 Q 945,256 897,256 h -1 -1 Q 847,256 792.5,280.5 738,305 706,329 674,353 623,396 465,528 197,734 160,764 128,800 V 32 Q 128,19 137.5,9.5 147,0 160,0 h 1472 q 13,0 22.5,9.5 9.5,9.5 9.5,22.5 z m 0,1051 v 11 13.5 q 0,0 -0.5,13 -0.5,13 -3,12.5 -2.5,-0.5 -5.5,9 -3,9.5 -9,7.5 -6,-2 -14,2.5 H 160 q -13,0 -22.5,-9.5 Q 128,1133 128,1120 128,952 275,836 468,684 676,519 682,514 711,489.5 740,465 757,452 774,439 801.5,420.5 829,402 852,393 q 23,-9 43,-9 h 1 1 q 20,0 43,9 23,9 50.5,27.5 27.5,18.5 44.5,31.5 17,13 46,37.5 29,24.5 35,29.5 208,165 401,317 54,43 100.5,115.5 46.5,72.5 46.5,131.5 z m 128,37 V 32 q 0,-66 -47,-113 -47,-47 -113,-47 H 160 Q 94,-128 47,-81 0,-34 0,32 v 1088 q 0,66 47,113 47,47 113,47 h 1472 q 66,0 113,-47 47,-47 47,-113 z"
203 d="m 1664,32 v 768 q -32,-36 -69,-66 -268,-206 -426,-338 -51,-43 -83,-67 -32,-24 -86.5,-48.5 Q 945,256 897,256 h -1 -1 Q 847,256 792.5,280.5 738,305 706,329 674,353 623,396 465,528 197,734 160,764 128,800 V 32 Q 128,19 137.5,9.5 147,0 160,0 h 1472 q 13,0 22.5,9.5 9.5,9.5 9.5,22.5 z m 0,1051 v 11 13.5 q 0,0 -0.5,13 -0.5,13 -3,12.5 -2.5,-0.5 -5.5,9 -3,9.5 -9,7.5 -6,-2 -14,2.5 H 160 q -13,0 -22.5,-9.5 Q 128,1133 128,1120 128,952 275,836 468,684 676,519 682,514 711,489.5 740,465 757,452 774,439 801.5,420.5 829,402 852,393 q 23,-9 43,-9 h 1 1 q 20,0 43,9 23,9 50.5,27.5 27.5,18.5 44.5,31.5 17,13 46,37.5 29,24.5 35,29.5 208,165 401,317 54,43 100.5,115.5 46.5,72.5 46.5,131.5 z m 128,37 V 32 q 0,-66 -47,-113 -47,-47 -113,-47 H 160 Q 94,-128 47,-81 0,-34 0,32 v 1088 q 0,66 47,113 47,47 113,47 h 1472 q 66,0 113,-47 47,-47 47,-113 z"
204 id="path2993"
204 id="path2993"
205 inkscape:connector-curvature="0"
205 inkscape:connector-curvature="0"
206 style="fill:currentColor" />
206 style="fill:currentColor" />
207 </g>
207 </g>
208 </svg>
208 </svg>
209 '''
209 '''
210
210
211
211 class EmailSettingsSchema(colander.Schema):
212 class EmailSettingsSchema(colander.Schema):
212 @colander.instantiate(validator=colander.Length(min=1))
213 @colander.instantiate(validator=colander.Length(min=1))
213 class recipients(colander.SequenceSchema):
214 class recipients(colander.SequenceSchema):
214 title = _('Recipients')
215 title = _('Recipients')
215 description = _('Email addresses to send push events to')
216 description = _('Email addresses to send push events to')
216 widget = deform.widget.SequenceWidget(min_len=1)
217 widget = deform.widget.SequenceWidget(min_len=1)
217
218
218 recipient = colander.SchemaNode(
219 recipient = colander.SchemaNode(
219 colander.String(),
220 colander.String(),
220 title=_('Email address'),
221 title=_('Email address'),
221 description=_('Email address'),
222 description=_('Email address'),
222 default='',
223 default='',
223 validator=colander.Email(),
224 validator=colander.Email(),
224 widget=deform.widget.TextInputWidget(
225 widget=deform.widget.TextInputWidget(
225 placeholder='user@domain.com',
226 placeholder='user@domain.com',
226 ),
227 ),
227 )
228 )
228
229
229
230
230 class EmailIntegrationType(IntegrationTypeBase):
231 class EmailIntegrationType(IntegrationTypeBase):
231 key = 'email'
232 key = 'email'
232 display_name = _('Email')
233 display_name = _('Email')
233 description = _('Send repo push summaries to a list of recipients via email')
234 description = _('Send repo push summaries to a list of recipients via email')
234 icon = email_icon
235 icon = email_icon
235
236
236 def settings_schema(self):
237 def settings_schema(self):
237 schema = EmailSettingsSchema()
238 schema = EmailSettingsSchema()
238 return schema
239 return schema
239
240
240 def send_event(self, event):
241 def send_event(self, event):
241 data = event.as_dict()
242 data = event.as_dict()
242 log.debug('got event: %r', event)
243 log.debug('got event: %r', event)
243
244
244 if isinstance(event, events.RepoPushEvent):
245 if isinstance(event, events.RepoPushEvent):
245 repo_push_handler(data, self.settings)
246 repo_push_handler(data, self.settings)
246 else:
247 else:
247 log.debug('ignoring event: %r', event)
248 log.debug('ignoring event: %r', event)
248
249
249
250
250 def repo_push_handler(data, settings):
251 def repo_push_handler(data, settings):
251 commit_num = len(data['push']['commits'])
252 commit_num = len(data['push']['commits'])
252 server_url = data['server_url']
253 server_url = data['server_url']
253
254
254 if commit_num == 0:
255 if commit_num == 1:
255 subject = '[{repo_name}] {author} pushed {commit_num} commit on branches: {branches}'.format(
256 if data['push']['branches']:
257 _subject = '[{repo_name}] {author} pushed {commit_num} commit on branches: {branches}'
258 else:
259 _subject = '[{repo_name}] {author} pushed {commit_num} commit'
260 subject = _subject.format(
256 author=data['actor']['username'],
261 author=data['actor']['username'],
257 repo_name=data['repo']['repo_name'],
262 repo_name=data['repo']['repo_name'],
258 commit_num=commit_num,
263 commit_num=commit_num,
259 branches=', '.join(
264 branches=', '.join(
260 branch['name'] for branch in data['push']['branches'])
265 branch['name'] for branch in data['push']['branches'])
261 )
266 )
262 else:
267 else:
263 subject = '[{repo_name}] {author} pushed {commit_num} commits on branches: {branches}'.format(
268 if data['push']['branches']:
269 _subject = '[{repo_name}] {author} pushed {commit_num} commits on branches: {branches}'
270 else:
271 _subject = '[{repo_name}] {author} pushed {commit_num} commits'
272 subject = _subject.format(
264 author=data['actor']['username'],
273 author=data['actor']['username'],
265 repo_name=data['repo']['repo_name'],
274 repo_name=data['repo']['repo_name'],
266 commit_num=commit_num,
275 commit_num=commit_num,
267 branches=', '.join(
276 branches=', '.join(
268 branch['name'] for branch in data['push']['branches']))
277 branch['name'] for branch in data['push']['branches']))
269
278
270 email_body_plaintext = repo_push_template_plaintext.render(
279 email_body_plaintext = repo_push_template_plaintext.render(
271 data=data,
280 data=data,
272 subject=subject,
281 subject=subject,
273 instance_url=server_url)
282 instance_url=server_url)
274
283
275 email_body_html = repo_push_template_html.render(
284 email_body_html = repo_push_template_html.render(
276 data=data,
285 data=data,
277 subject=subject,
286 subject=subject,
278 instance_url=server_url)
287 instance_url=server_url)
279
288
280 for email_address in settings['recipients']:
289 for email_address in settings['recipients']:
281 run_task(
290 run_task(
282 tasks.send_email, email_address, subject,
291 tasks.send_email, email_address, subject,
283 email_body_plaintext, email_body_html)
292 email_body_plaintext, email_body_html)
General Comments 0
You need to be logged in to leave comments. Login now