Show More
@@ -1,127 +1,222 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2012-2016 RhodeCode GmbH |
|
3 | # Copyright (C) 2012-2016 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 |
|
||||
23 | import deform |
|
22 | import deform | |
24 | import logging |
|
23 | import logging | |
25 | import colander |
|
24 | import colander | |
26 |
|
25 | |||
27 | from mako.template import Template |
|
26 | from mako.template import Template | |
28 |
|
27 | |||
29 | from rhodecode import events |
|
28 | from rhodecode import events | |
30 | from rhodecode.translation import _, lazy_ugettext |
|
29 | from rhodecode.translation import _, lazy_ugettext | |
31 | from rhodecode.lib.celerylib import run_task |
|
30 | from rhodecode.lib.celerylib import run_task | |
32 | from rhodecode.lib.celerylib import tasks |
|
31 | from rhodecode.lib.celerylib import tasks | |
33 | from rhodecode.integrations.types.base import IntegrationTypeBase |
|
32 | from rhodecode.integrations.types.base import IntegrationTypeBase | |
34 | from rhodecode.integrations.schema import IntegrationSettingsSchemaBase |
|
33 | from rhodecode.integrations.schema import IntegrationSettingsSchemaBase | |
35 |
|
34 | |||
36 |
|
35 | |||
37 | log = logging.getLogger() |
|
36 | log = logging.getLogger(__name__) | |
|
37 | ||||
|
38 | repo_push_template_plaintext = Template(''' | |||
|
39 | Commits: | |||
|
40 | ||||
|
41 | % for commit in data['push']['commits']: | |||
|
42 | ${commit['url']} by ${commit['author']} at ${commit['date']} | |||
|
43 | ${commit['message']} | |||
|
44 | ---- | |||
|
45 | ||||
|
46 | % endfor | |||
|
47 | ''') | |||
|
48 | ||||
|
49 | ## TODO (marcink): think about putting this into a file, or use base.mako email template | |||
|
50 | ||||
|
51 | repo_push_template_html = Template(''' | |||
|
52 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |||
|
53 | <html xmlns="http://www.w3.org/1999/xhtml"> | |||
|
54 | <head> | |||
|
55 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |||
|
56 | <meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |||
|
57 | <title>${subject}</title> | |||
|
58 | <style type="text/css"> | |||
|
59 | /* Based on The MailChimp Reset INLINE: Yes. */ | |||
|
60 | #outlook a {padding:0;} /* Force Outlook to provide a "view in browser" menu link. */ | |||
|
61 | body{width:100% !important; -webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; margin:0; padding:0;} | |||
|
62 | /* Prevent Webkit and Windows Mobile platforms from changing default font sizes.*/ | |||
|
63 | .ExternalClass {width:100%;} /* Force Hotmail to display emails at full width */ | |||
|
64 | .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;} | |||
|
65 | /* Forces Hotmail to display normal line spacing. More on that: http://www.emailonacid.com/forum/viewthread/43/ */ | |||
|
66 | #backgroundTable {margin:0; padding:0; line-height: 100% !important;} | |||
|
67 | /* End reset */ | |||
|
68 | ||||
|
69 | /* defaults for images*/ | |||
|
70 | img {outline:none; text-decoration:none; -ms-interpolation-mode: bicubic;} | |||
|
71 | a img {border:none;} | |||
|
72 | .image_fix {display:block;} | |||
|
73 | ||||
|
74 | body {line-height:1.2em;} | |||
|
75 | p {margin: 0 0 20px;} | |||
|
76 | h1, h2, h3, h4, h5, h6 {color:#323232!important;} | |||
|
77 | a {color:#427cc9;text-decoration:none;outline:none;cursor:pointer;} | |||
|
78 | a:focus {outline:none;} | |||
|
79 | a:hover {color: #305b91;} | |||
|
80 | h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {color:#427cc9!important;text-decoration:none!important;} | |||
|
81 | h1 a:active, h2 a:active, h3 a:active, h4 a:active, h5 a:active, h6 a:active {color: #305b91!important;} | |||
|
82 | h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited {color: #305b91!important;} | |||
|
83 | table {font-size:13px;border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt;} | |||
|
84 | table td {padding:.65em 1em .65em 0;border-collapse:collapse;vertical-align:top;text-align:left;} | |||
|
85 | input {display:inline;border-radius:2px;border-style:solid;border: 1px solid #dbd9da;padding:.5em;} | |||
|
86 | input:focus {outline: 1px solid #979797} | |||
|
87 | @media only screen and (-webkit-min-device-pixel-ratio: 2) { | |||
|
88 | /* Put your iPhone 4g styles in here */ | |||
|
89 | } | |||
38 |
|
90 | |||
|
91 | /* Android targeting */ | |||
|
92 | @media only screen and (-webkit-device-pixel-ratio:.75){ | |||
|
93 | /* Put CSS for low density (ldpi) Android layouts in here */ | |||
|
94 | } | |||
|
95 | @media only screen and (-webkit-device-pixel-ratio:1){ | |||
|
96 | /* Put CSS for medium density (mdpi) Android layouts in here */ | |||
|
97 | } | |||
|
98 | @media only screen and (-webkit-device-pixel-ratio:1.5){ | |||
|
99 | /* Put CSS for high density (hdpi) Android layouts in here */ | |||
|
100 | } | |||
|
101 | /* end Android targeting */ | |||
|
102 | ||||
|
103 | </style> | |||
|
104 | ||||
|
105 | <!-- Targeting Windows Mobile --> | |||
|
106 | <!--[if IEMobile 7]> | |||
|
107 | <style type="text/css"> | |||
|
108 | ||||
|
109 | </style> | |||
|
110 | <![endif]--> | |||
|
111 | ||||
|
112 | <!--[if gte mso 9]> | |||
|
113 | <style> | |||
|
114 | /* Target Outlook 2007 and 2010 */ | |||
|
115 | </style> | |||
|
116 | <![endif]--> | |||
|
117 | </head> | |||
|
118 | <body> | |||
|
119 | <!-- 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. --> | |||
|
120 | <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"> | |||
|
121 | <tr> | |||
|
122 | <td valign="top" style="padding:0;"> | |||
|
123 | <table cellpadding="0" cellspacing="0" border="0" align="left" width="100%"> | |||
|
124 | <tr><td style="width:100%;padding:7px;background-color:#202020" valign="top"> | |||
|
125 | <a style="color:#eeeeee;text-decoration:none;" href="${instance_url}"> | |||
|
126 | ${'RhodeCode'} | |||
|
127 | </a> | |||
|
128 | </td></tr> | |||
|
129 | <tr> | |||
|
130 | <td style="padding:15px;" valign="top"> | |||
|
131 | % for commit in data['push']['commits']: | |||
|
132 | <a href="${commit['url']}">${commit['short_id']}</a> by ${commit['author']} at ${commit['date']} <br/> | |||
|
133 | ${commit['message_html']} <br/> | |||
|
134 | <br/> | |||
|
135 | % endfor | |||
|
136 | </td> | |||
|
137 | </tr> | |||
|
138 | </table> | |||
|
139 | </td> | |||
|
140 | </tr> | |||
|
141 | </table> | |||
|
142 | <!-- End of wrapper table --> | |||
|
143 | <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}"> | |||
|
144 | ${'This is a notification from RhodeCode. %(instance_url)s' % {'instance_url': instance_url}} | |||
|
145 | </a></p> | |||
|
146 | </body> | |||
|
147 | </html> | |||
|
148 | ''') | |||
39 |
|
149 | |||
40 |
|
150 | |||
41 | class EmailSettingsSchema(IntegrationSettingsSchemaBase): |
|
151 | class EmailSettingsSchema(IntegrationSettingsSchemaBase): | |
42 | @colander.instantiate(validator=colander.Length(min=1)) |
|
152 | @colander.instantiate(validator=colander.Length(min=1)) | |
43 | class recipients(colander.SequenceSchema): |
|
153 | class recipients(colander.SequenceSchema): | |
44 | title = lazy_ugettext('Recipients') |
|
154 | title = lazy_ugettext('Recipients') | |
45 | description = lazy_ugettext('Email addresses to send push events to') |
|
155 | description = lazy_ugettext('Email addresses to send push events to') | |
46 | widget = deform.widget.SequenceWidget(min_len=1) |
|
156 | widget = deform.widget.SequenceWidget(min_len=1) | |
47 |
|
157 | |||
48 | recipient = colander.SchemaNode( |
|
158 | recipient = colander.SchemaNode( | |
49 | colander.String(), |
|
159 | colander.String(), | |
50 | title=lazy_ugettext('Email address'), |
|
160 | title=lazy_ugettext('Email address'), | |
51 | description=lazy_ugettext('Email address'), |
|
161 | description=lazy_ugettext('Email address'), | |
52 | default='', |
|
162 | default='', | |
53 | validator=colander.Email(), |
|
163 | validator=colander.Email(), | |
54 | widget=deform.widget.TextInputWidget( |
|
164 | widget=deform.widget.TextInputWidget( | |
55 | placeholder='user@domain.com', |
|
165 | placeholder='user@domain.com', | |
56 | ), |
|
166 | ), | |
57 | ) |
|
167 | ) | |
58 |
|
168 | |||
59 |
|
169 | |||
60 | class EmailIntegrationType(IntegrationTypeBase): |
|
170 | class EmailIntegrationType(IntegrationTypeBase): | |
61 | key = 'email' |
|
171 | key = 'email' | |
62 | display_name = lazy_ugettext('Email') |
|
172 | display_name = lazy_ugettext('Email') | |
63 | SettingsSchema = EmailSettingsSchema |
|
173 | SettingsSchema = EmailSettingsSchema | |
64 |
|
174 | |||
65 | def settings_schema(self): |
|
175 | def settings_schema(self): | |
66 | schema = EmailSettingsSchema() |
|
176 | schema = EmailSettingsSchema() | |
67 | return schema |
|
177 | return schema | |
68 |
|
178 | |||
69 | def send_event(self, event): |
|
179 | def send_event(self, event): | |
70 | data = event.as_dict() |
|
180 | data = event.as_dict() | |
71 | log.debug('got event: %r', event) |
|
181 | log.debug('got event: %r', event) | |
72 |
|
182 | |||
73 | if isinstance(event, events.RepoPushEvent): |
|
183 | if isinstance(event, events.RepoPushEvent): | |
74 | repo_push_handler(data, self.settings) |
|
184 | repo_push_handler(data, self.settings) | |
75 | else: |
|
185 | else: | |
76 | log.debug('ignoring event: %r', event) |
|
186 | log.debug('ignoring event: %r', event) | |
77 |
|
187 | |||
78 |
|
188 | |||
79 | def repo_push_handler(data, settings): |
|
189 | def repo_push_handler(data, settings): | |
80 |
|
|
190 | commit_num = len(data['push']['commits']) | |
81 | email_body_plaintext = repo_push_template_plaintext.render( |
|
191 | server_url = data['server_url'] | |
82 | data=data, |
|
|||
83 | commit=commit, |
|
|||
84 | commit_msg=commit['message'], |
|
|||
85 | ) |
|
|||
86 | email_body_html = repo_push_template_html.render( |
|
|||
87 | data=data, |
|
|||
88 | commit=commit, |
|
|||
89 | commit_msg=commit['message_html'], |
|
|||
90 | ) |
|
|||
91 |
|
||||
92 | subject = '[%(repo_name)s] %(commit_id)s: %(commit_msg)s' % { |
|
|||
93 | 'repo_name': data['repo']['repo_name'], |
|
|||
94 | 'commit_id': commit['short_id'], |
|
|||
95 | 'commit_msg': commit['message'].split('\n')[0][:150] |
|
|||
96 | } |
|
|||
97 | for email_address in settings['recipients']: |
|
|||
98 | task = run_task( |
|
|||
99 | tasks.send_email, email_address, subject, |
|
|||
100 | email_body_plaintext, email_body_html) |
|
|||
101 |
|
||||
102 |
|
192 | |||
103 | # TODO: dan: add changed files, make html pretty |
|
193 | if commit_num == 0: | |
104 | repo_push_template_plaintext = Template(''' |
|
194 | subject = '[{repo_name}] {author} pushed {commit_num} commit on branches: {branches}'.format( | |
105 |
|
|
195 | author=data['actor']['username'], | |
106 | Branches: ${', '.join(branch['name'] for branch in data['push']['branches'])} |
|
196 | repo_name=data['repo']['repo_name'], | |
107 | Repository: ${data['repo']['url']} |
|
197 | commit_num=commit_num, | |
108 | Commit: ${commit['raw_id']} |
|
198 | branches=', '.join( | |
109 | URL: ${commit['url']} |
|
199 | branch['name'] for branch in data['push']['branches']) | |
110 | Author: ${commit['author']} |
|
200 | ) | |
111 | Date: ${commit['date']} |
|
201 | else: | |
112 | Commit Message: |
|
202 | subject = '[{repo_name}] {author} pushed {commit_num} commits on branches: {branches}'.format( | |
|
203 | author=data['actor']['username'], | |||
|
204 | repo_name=data['repo']['repo_name'], | |||
|
205 | commit_num=commit_num, | |||
|
206 | branches=', '.join( | |||
|
207 | branch['name'] for branch in data['push']['branches'])) | |||
113 |
|
208 | |||
114 | ${commit_msg} |
|
209 | email_body_plaintext = repo_push_template_plaintext.render( | |
115 | ''') |
|
210 | data=data, | |
|
211 | subject=subject, | |||
|
212 | instance_url=server_url) | |||
116 |
|
213 | |||
117 | repo_push_template_html = Template(''' |
|
214 | email_body_html = repo_push_template_html.render( | |
118 | User: ${data['actor']['username']}<br> |
|
215 | data=data, | |
119 | Branches: ${', '.join(branch['name'] for branch in data['push']['branches'])}<br> |
|
216 | subject=subject, | |
120 | Repository: ${data['repo']['url']}<br> |
|
217 | instance_url=server_url) | |
121 | Commit: ${commit['raw_id']}<br> |
|
218 | ||
122 | URL: ${commit['url']}<br> |
|
219 | for email_address in settings['recipients']: | |
123 | Author: ${commit['author']}<br> |
|
220 | run_task( | |
124 | Date: ${commit['date']}<br> |
|
221 | tasks.send_email, email_address, subject, | |
125 | Commit Message:<br> |
|
222 | email_body_plaintext, email_body_html) | |
126 | <p>${commit_msg}</p> |
|
|||
127 | ''') |
|
General Comments 0
You need to be logged in to leave comments.
Login now