Show More
@@ -1,235 +1,235 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | # This program is free software: you can redistribute it and/or modify |
|
3 | 3 | # it under the terms of the GNU General Public License as published by |
|
4 | 4 | # the Free Software Foundation, either version 3 of the License, or |
|
5 | 5 | # (at your option) any later version. |
|
6 | 6 | # |
|
7 | 7 | # This program is distributed in the hope that it will be useful, |
|
8 | 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
9 | 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
10 | 10 | # GNU General Public License for more details. |
|
11 | 11 | # |
|
12 | 12 | # You should have received a copy of the GNU General Public License |
|
13 | 13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
14 | 14 | """ |
|
15 | 15 | kallithea.model.notification |
|
16 | 16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
17 | 17 | |
|
18 | 18 | Model for notifications |
|
19 | 19 | |
|
20 | 20 | |
|
21 | 21 | This file was forked by the Kallithea project in July 2014. |
|
22 | 22 | Original author and date, and relevant copyright and licensing information is below: |
|
23 | 23 | :created_on: Nov 20, 2011 |
|
24 | 24 | :author: marcink |
|
25 | 25 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
26 | 26 | :license: GPLv3, see LICENSE.md for more details. |
|
27 | 27 | """ |
|
28 | 28 | |
|
29 | 29 | import datetime |
|
30 | 30 | import logging |
|
31 | 31 | |
|
32 | 32 | from tg import app_globals |
|
33 | 33 | from tg import tmpl_context as c |
|
34 | 34 | from tg.i18n import ugettext as _ |
|
35 | 35 | |
|
36 | 36 | from kallithea.lib import helpers as h |
|
37 | 37 | from kallithea.model.db import User |
|
38 | 38 | |
|
39 | 39 | |
|
40 | 40 | log = logging.getLogger(__name__) |
|
41 | 41 | |
|
42 | 42 | |
|
43 | 43 | class NotificationModel(object): |
|
44 | 44 | |
|
45 | 45 | TYPE_CHANGESET_COMMENT = 'cs_comment' |
|
46 | 46 | TYPE_MESSAGE = 'message' |
|
47 | 47 | TYPE_MENTION = 'mention' # not used |
|
48 | 48 | TYPE_REGISTRATION = 'registration' |
|
49 | 49 | TYPE_PULL_REQUEST = 'pull_request' |
|
50 | 50 | TYPE_PULL_REQUEST_COMMENT = 'pull_request_comment' |
|
51 | 51 | |
|
52 | 52 | def create(self, created_by, subject, body, recipients=None, |
|
53 | 53 | type_=TYPE_MESSAGE, with_email=True, |
|
54 | 54 | email_kwargs=None, repo_name=None): |
|
55 | 55 | """ |
|
56 | 56 | |
|
57 | 57 | Creates notification of given type |
|
58 | 58 | |
|
59 | 59 | :param created_by: int, str or User instance. User who created this |
|
60 | 60 | notification |
|
61 | 61 | :param subject: |
|
62 | 62 | :param body: |
|
63 | 63 | :param recipients: list of int, str or User objects, when None |
|
64 | 64 | is given send to all admins |
|
65 | 65 | :param type_: type of notification |
|
66 | 66 | :param with_email: send email with this notification |
|
67 | 67 | :param email_kwargs: additional dict to pass as args to email template |
|
68 | 68 | """ |
|
69 | 69 | from kallithea.lib.celerylib import tasks |
|
70 | 70 | email_kwargs = email_kwargs or {} |
|
71 | 71 | if recipients and not getattr(recipients, '__iter__', False): |
|
72 | 72 | raise Exception('recipients must be a list or iterable') |
|
73 | 73 | |
|
74 | 74 | created_by_obj = User.guess_instance(created_by) |
|
75 | 75 | |
|
76 | 76 | recipients_objs = set() |
|
77 | 77 | if recipients: |
|
78 | 78 | for u in recipients: |
|
79 | 79 | obj = User.guess_instance(u) |
|
80 | 80 | if obj is not None: |
|
81 | 81 | recipients_objs.add(obj) |
|
82 | 82 | else: |
|
83 | 83 | # TODO: inform user that requested operation couldn't be completed |
|
84 | 84 | log.error('cannot email unknown user %r', u) |
|
85 | 85 | log.debug('sending notifications %s to %s', |
|
86 | 86 | type_, recipients_objs |
|
87 | 87 | ) |
|
88 | 88 | elif recipients is None: |
|
89 | 89 | # empty recipients means to all admins |
|
90 | 90 | recipients_objs = User.query().filter(User.admin == True).all() |
|
91 | 91 | log.debug('sending notifications %s to admins: %s', |
|
92 | 92 | type_, recipients_objs |
|
93 | 93 | ) |
|
94 | 94 | #else: silently skip notification mails? |
|
95 | 95 | |
|
96 | 96 | if not with_email: |
|
97 | 97 | return |
|
98 | 98 | |
|
99 | 99 | headers = {} |
|
100 | 100 | headers['X-Kallithea-Notification-Type'] = type_ |
|
101 | 101 | if 'threading' in email_kwargs: |
|
102 | 102 | headers['References'] = ' '.join('<%s>' % x for x in email_kwargs['threading']) |
|
103 | 103 | |
|
104 | 104 | # this is passed into template |
|
105 | 105 | created_on = h.fmt_date(datetime.datetime.now()) |
|
106 | 106 | html_kwargs = { |
|
107 | 107 | 'subject': subject, |
|
108 | 108 | 'body': h.render_w_mentions(body, repo_name), |
|
109 | 109 | 'when': created_on, |
|
110 | 110 | 'user': created_by_obj.username, |
|
111 | 111 | } |
|
112 | 112 | |
|
113 | 113 | txt_kwargs = { |
|
114 | 114 | 'subject': subject, |
|
115 | 115 | 'body': body, |
|
116 | 116 | 'when': created_on, |
|
117 | 117 | 'user': created_by_obj.username, |
|
118 | 118 | } |
|
119 | 119 | |
|
120 | 120 | html_kwargs.update(email_kwargs) |
|
121 | 121 | txt_kwargs.update(email_kwargs) |
|
122 | 122 | email_subject = EmailNotificationModel() \ |
|
123 | 123 | .get_email_description(type_, **txt_kwargs) |
|
124 | 124 | email_txt_body = EmailNotificationModel() \ |
|
125 | 125 | .get_email_tmpl(type_, 'txt', **txt_kwargs) |
|
126 | 126 | email_html_body = EmailNotificationModel() \ |
|
127 | 127 | .get_email_tmpl(type_, 'html', **html_kwargs) |
|
128 | 128 | |
|
129 | 129 | # don't send email to the person who caused the notification, except for |
|
130 | 130 | # notifications about new pull requests where the author is explicitly |
|
131 | 131 | # added. |
|
132 | 132 | rec_mails = set(obj.email for obj in recipients_objs) |
|
133 | 133 | if type_ == NotificationModel.TYPE_PULL_REQUEST: |
|
134 | 134 | rec_mails.add(created_by_obj.email) |
|
135 | 135 | else: |
|
136 | 136 | rec_mails.discard(created_by_obj.email) |
|
137 | 137 | |
|
138 | 138 | # send email with notification to participants |
|
139 | 139 | for rec_mail in sorted(rec_mails): |
|
140 | 140 | tasks.send_email([rec_mail], email_subject, email_txt_body, |
|
141 | 141 | email_html_body, headers, |
|
142 | 142 | from_name=created_by_obj.full_name_or_username) |
|
143 | 143 | |
|
144 | 144 | |
|
145 | 145 | class EmailNotificationModel(object): |
|
146 | 146 | |
|
147 | 147 | TYPE_CHANGESET_COMMENT = NotificationModel.TYPE_CHANGESET_COMMENT |
|
148 | 148 | TYPE_MESSAGE = NotificationModel.TYPE_MESSAGE # only used for testing |
|
149 | 149 | # NotificationModel.TYPE_MENTION is not used |
|
150 | 150 | TYPE_PASSWORD_RESET = 'password_link' |
|
151 | 151 | TYPE_REGISTRATION = NotificationModel.TYPE_REGISTRATION |
|
152 | 152 | TYPE_PULL_REQUEST = NotificationModel.TYPE_PULL_REQUEST |
|
153 | 153 | TYPE_PULL_REQUEST_COMMENT = NotificationModel.TYPE_PULL_REQUEST_COMMENT |
|
154 | 154 | TYPE_DEFAULT = 'default' |
|
155 | 155 | |
|
156 | 156 | def __init__(self): |
|
157 | 157 | super(EmailNotificationModel, self).__init__() |
|
158 | 158 | self._tmpl_lookup = app_globals.mako_lookup |
|
159 | 159 | self.email_types = { |
|
160 | 160 | self.TYPE_CHANGESET_COMMENT: 'changeset_comment', |
|
161 | 161 | self.TYPE_PASSWORD_RESET: 'password_reset', |
|
162 | 162 | self.TYPE_REGISTRATION: 'registration', |
|
163 | 163 | self.TYPE_DEFAULT: 'default', |
|
164 | 164 | self.TYPE_PULL_REQUEST: 'pull_request', |
|
165 | 165 | self.TYPE_PULL_REQUEST_COMMENT: 'pull_request_comment', |
|
166 | 166 | } |
|
167 | 167 | self._subj_map = { |
|
168 | 168 | self.TYPE_CHANGESET_COMMENT: _('[Comment] %(repo_name)s changeset %(short_id)s "%(message_short)s" on %(branch)s by %(cs_author_username)s'), |
|
169 | 169 | self.TYPE_MESSAGE: 'Test Message', |
|
170 | 170 | # self.TYPE_PASSWORD_RESET |
|
171 | 171 | self.TYPE_REGISTRATION: _('New user %(new_username)s registered'), |
|
172 | 172 | # self.TYPE_DEFAULT |
|
173 | 173 | self.TYPE_PULL_REQUEST: _('[Review] %(repo_name)s PR %(pr_nice_id)s "%(pr_title_short)s" from %(pr_source_branch)s by %(pr_owner_username)s'), |
|
174 | 174 | self.TYPE_PULL_REQUEST_COMMENT: _('[Comment] %(repo_name)s PR %(pr_nice_id)s "%(pr_title_short)s" from %(pr_source_branch)s by %(pr_owner_username)s'), |
|
175 | 175 | } |
|
176 | 176 | |
|
177 | 177 | def get_email_description(self, type_, **kwargs): |
|
178 | 178 | """ |
|
179 | 179 | return subject for email based on given type |
|
180 | 180 | """ |
|
181 | 181 | tmpl = self._subj_map[type_] |
|
182 | 182 | try: |
|
183 | 183 | subj = tmpl % kwargs |
|
184 | 184 | except KeyError as e: |
|
185 | 185 | log.error('error generating email subject for %r from %s: %s', type_, ', '.join(self._subj_map), e) |
|
186 | 186 | raise |
|
187 | 187 | # gmail doesn't do proper threading but will ignore leading square |
|
188 | 188 | # bracket content ... so that is where we put status info |
|
189 | 189 | bracket_tags = [] |
|
190 | 190 | status_change = kwargs.get('status_change') |
|
191 | 191 | if status_change: |
|
192 | 192 | bracket_tags.append(str(status_change)) # apply str to evaluate LazyString before .join |
|
193 | 193 | if kwargs.get('closing_pr'): |
|
194 | 194 | bracket_tags.append(_('Closing')) |
|
195 | 195 | if bracket_tags: |
|
196 | 196 | if subj.startswith('['): |
|
197 | 197 | subj = '[' + ', '.join(bracket_tags) + ': ' + subj[1:] |
|
198 | 198 | else: |
|
199 | 199 | subj = '[' + ', '.join(bracket_tags) + '] ' + subj |
|
200 | 200 | return subj |
|
201 | 201 | |
|
202 | 202 | def get_email_tmpl(self, type_, content_type, **kwargs): |
|
203 | 203 | """ |
|
204 | 204 | return generated template for email based on given type |
|
205 | 205 | """ |
|
206 | 206 | |
|
207 |
base = 'email |
|
|
207 | base = 'email/' + self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT]) + '.' + content_type | |
|
208 | 208 | email_template = self._tmpl_lookup.get_template(base) |
|
209 | 209 | # translator and helpers inject |
|
210 | 210 | _kwargs = {'_': _, |
|
211 | 211 | 'h': h, |
|
212 | 212 | 'c': c} |
|
213 | 213 | _kwargs.update(kwargs) |
|
214 | 214 | if content_type == 'html': |
|
215 | 215 | _kwargs.update({ |
|
216 | 216 | "color_text": "#202020", |
|
217 | 217 | "color_emph": "#395fa0", |
|
218 | 218 | "color_link": "#395fa0", |
|
219 | 219 | "color_border": "#ddd", |
|
220 | 220 | "color_background_grey": "#f9f9f9", |
|
221 | 221 | "color_button": "#395fa0", |
|
222 | 222 | "monospace_style": "font-family:Lucida Console,Consolas,Monaco,Inconsolata,Liberation Mono,monospace", |
|
223 | 223 | "sans_style": "font-family:Helvetica,Arial,sans-serif", |
|
224 | 224 | }) |
|
225 | 225 | _kwargs.update({ |
|
226 | 226 | "default_style": "%(sans_style)s;font-weight:200;font-size:14px;line-height:17px;color:%(color_text)s" % _kwargs, |
|
227 | 227 | "comment_style": "%(monospace_style)s;white-space:pre-wrap" % _kwargs, |
|
228 | 228 | "data_style": "border:%(color_border)s 1px solid;background:%(color_background_grey)s" % _kwargs, |
|
229 | 229 | "emph_style": "font-weight:600;color:%(color_emph)s" % _kwargs, |
|
230 | 230 | "link_style": "color:%(color_link)s;text-decoration:none" % _kwargs, |
|
231 | 231 | "link_text_style": "color:%(color_text)s;text-decoration:none;border:%(color_border)s 1px solid;background:%(color_background_grey)s" % _kwargs, |
|
232 | 232 | }) |
|
233 | 233 | |
|
234 | 234 | log.debug('rendering tmpl %s with kwargs %s', base, _kwargs) |
|
235 | 235 | return email_template.render_unicode(**_kwargs) |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/button.html to kallithea/templates/email/button.html |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/button.txt to kallithea/templates/email/button.txt |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/changeset_comment.html to kallithea/templates/email/changeset_comment.html |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/changeset_comment.txt to kallithea/templates/email/changeset_comment.txt |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/comment.html to kallithea/templates/email/comment.html |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/comment.txt to kallithea/templates/email/comment.txt |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/default.html to kallithea/templates/email/default.html |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/default.txt to kallithea/templates/email/default.txt |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/header.html to kallithea/templates/email/header.html |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/header.txt to kallithea/templates/email/header.txt |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/main.html to kallithea/templates/email/main.html |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/password_reset.html to kallithea/templates/email/password_reset.html |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/password_reset.txt to kallithea/templates/email/password_reset.txt |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/pull_request.html to kallithea/templates/email/pull_request.html |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/pull_request.txt to kallithea/templates/email/pull_request.txt |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/pull_request_comment.html to kallithea/templates/email/pull_request_comment.html |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/pull_request_comment.txt to kallithea/templates/email/pull_request_comment.txt |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/registration.html to kallithea/templates/email/registration.html |
|
1 | NO CONTENT: file renamed from kallithea/templates/email_templates/registration.txt to kallithea/templates/email/registration.txt |
@@ -1,24 +1,24 b'' | |||
|
1 | 1 | #!/bin/bash -xe |
|
2 | 2 | |
|
3 | 3 | # Enforce some consistency in whitespace - just to avoid spurious whitespaces changes |
|
4 | 4 | |
|
5 |
files=`hg mani | egrep -v '/fontello/|/ |
|
|
5 | files=`hg mani | egrep -v '/fontello/|/templates/email/|(^LICENSE-MERGELY.html|^docs/Makefile|^scripts/whitespacecleanup.sh|/(graph|mergely|native.history)\.js|/test_dump_html_mails.ref.html|\.png|\.gif|\.ico|\.pot|\.po|\.mo|\.tar\.gz|\.diff)$'` | |
|
6 | 6 | |
|
7 | 7 | sed -i "s/`printf '\r'`//g" $files |
|
8 | 8 | sed -i -e "s,`printf '\t'`, ,g" $files |
|
9 | 9 | sed -i -e "s, *$,,g" $files |
|
10 | 10 | sed -i -e 's,\([^ ]\)\\$,\1 \\,g' -e 's,\(["'"'"']["'"'"']["'"'"']\) \\$,\1\\,g' $files |
|
11 | 11 | # ensure one trailing newline - remove empty last line and make last line include trailing newline: |
|
12 | 12 | sed -i -e '$,${/^$/d}' -e '$a\' $files |
|
13 | 13 | |
|
14 | 14 | sed -i -e 's,\([^ /]\){,\1 {,g' `hg loc '*.css'` |
|
15 | 15 | sed -i -e 's|^\([^ /].*,\)\([^ ]\)|\1 \2|g' `hg loc '*.css'` |
|
16 | 16 | |
|
17 | 17 | hg mani | xargs chmod -x |
|
18 | 18 | hg loc 'set:!binary()&grep("^#!")&!(**_tmpl.py)&!(**/template**)' | xargs chmod +x |
|
19 | 19 | |
|
20 | 20 | # isort is installed from dev_requirements.txt |
|
21 | 21 | hg loc 'set:!binary()&grep("^#!.*python")' '*.py' | xargs isort --line-width 160 --lines-after-imports 2 |
|
22 | 22 | |
|
23 | 23 | echo "diff after $0:" |
|
24 | 24 | hg diff |
General Comments 0
You need to be logged in to leave comments.
Login now