##// END OF EJS Templates
emails: updated emails design and data structure they provide....
dan -
r4038:4a4a02a9 default
parent child Browse files
Show More
@@ -0,0 +1,29 b''
1 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2
3 <html>
4 <head></head>
5
6 <body>
7
8 SUBJECT:
9 <pre>${c.subject}</pre>
10
11 HEADERS:
12 <pre>
13 ${c.headers}
14 </pre>
15
16 PLAINTEXT:
17 <pre>
18 ${c.email_body_plaintext|n}
19 </pre>
20
21 </body>
22 </html>
23 <br/><br/>
24
25 HTML:
26
27 ${c.email_body|n}
28
29
@@ -0,0 +1,49 b''
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
3
4 <%def name="title()">
5 ${_('Show notification')} ${c.rhodecode_user.username}
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
9 </%def>
10
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('My Notifications'), h.route_path('notifications_show_all'))}
13 &raquo;
14 ${_('Show notification')}
15 </%def>
16
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
19 </%def>
20
21 <%def name="main()">
22 <div class="box">
23
24 <!-- box / title -->
25 <div class="title">
26 Rendered plain text using markup renderer
27 </div>
28 <div class="table">
29 <div >
30 <div class="notification-header">
31 GRAVATAR
32 <div class="desc">
33 DESC
34 </div>
35 </div>
36 <div class="notification-body">
37 <div class="notification-subject">
38 <h3>${_('Subject')}: ${c.subject}</h3>
39 </div>
40 ${c.email_body|n}
41 </div>
42 </div>
43 </div>
44 </div>
45
46 </%def>
47
48
49
@@ -0,0 +1,34 b''
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/debug_style/index.html"/>
3
4 <%def name="breadcrumbs_links()">
5 ${h.link_to(_('Style'), h.route_path('debug_style_home'))}
6 &raquo;
7 ${c.active}
8 </%def>
9
10
11 <%def name="real_main()">
12 <div class="box">
13 <div class="title">
14 ${self.breadcrumbs()}
15 </div>
16
17 <div class='sidebar-col-wrapper'>
18 ${self.sidebar()}
19 <div class="main-content">
20 <h2>Emails</h2>
21 <ul>
22 % for elem in sorted(c.email_types.keys()):
23 <li>
24 <a href="${request.route_path('debug_style_email', email_id=elem, _query={'user':c.rhodecode_user.username})}">${elem}</a>
25 |
26 <a href="${request.route_path('debug_style_email_plain_rendered', email_id=elem, _query={'user':c.rhodecode_user.username})}">plain rendered</a>
27 </li>
28 % endfor
29 </ul>
30
31 </div> <!-- .main-content -->
32 </div>
33 </div> <!-- .box -->
34 </%def>
@@ -43,6 +43,14 b' def includeme(config):'
43 43 pattern=ADMIN_PREFIX + '/debug_style',
44 44 debug_style=True)
45 45 config.add_route(
46 name='debug_style_email',
47 pattern=ADMIN_PREFIX + '/debug_style/email/{email_id}',
48 debug_style=True)
49 config.add_route(
50 name='debug_style_email_plain_rendered',
51 pattern=ADMIN_PREFIX + '/debug_style/email-rendered/{email_id}',
52 debug_style=True)
53 config.add_route(
46 54 name='debug_style_template',
47 55 pattern=ADMIN_PREFIX + '/debug_style/t/{t_path}',
48 56 debug_style=True)
@@ -20,10 +20,15 b''
20 20
21 21 import os
22 22 import logging
23 import datetime
23 24
24 25 from pyramid.view import view_config
25 26 from pyramid.renderers import render_to_response
26 27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib.celerylib import run_task, tasks
29 from rhodecode.lib.utils2 import AttributeDict
30 from rhodecode.model.db import User
31 from rhodecode.model.notification import EmailNotificationModel
27 32
28 33 log = logging.getLogger(__name__)
29 34
@@ -46,6 +51,271 b' class DebugStyleView(BaseAppView):'
46 51 request=self.request)
47 52
48 53 @view_config(
54 route_name='debug_style_email', request_method='GET',
55 renderer=None)
56 @view_config(
57 route_name='debug_style_email_plain_rendered', request_method='GET',
58 renderer=None)
59 def render_email(self):
60 c = self.load_default_context()
61 email_id = self.request.matchdict['email_id']
62 c.active = 'emails'
63
64 pr = AttributeDict(
65 pull_request_id=123,
66 title='digital_ocean: fix redis, elastic search start on boot, '
67 'fix fd limits on supervisor, set postgres 11 version',
68 description='''
69 Check if we should use full-topic or mini-topic.
70
71 - full topic produces some problems with merge states etc
72 - server-mini-topic needs probably tweeks.
73 ''',
74 repo_name='foobar',
75 source_ref_parts=AttributeDict(type='branch', name='fix-ticket-2000'),
76 target_ref_parts=AttributeDict(type='branch', name='master'),
77 )
78 target_repo = AttributeDict(repo_name='repo_group/target_repo')
79 source_repo = AttributeDict(repo_name='repo_group/source_repo')
80 user = User.get_by_username(self.request.GET.get('user')) or self._rhodecode_db_user
81
82 email_kwargs = {
83 'test': {},
84 'message': {
85 'body': 'message body !'
86 },
87 'email_test': {
88 'user': user,
89 'date': datetime.datetime.now(),
90 'rhodecode_version': c.rhodecode_version
91 },
92 'password_reset': {
93 'password_reset_url': 'http://example.com/reset-rhodecode-password/token',
94
95 'user': user,
96 'date': datetime.datetime.now(),
97 'email': 'test@rhodecode.com',
98 'first_admin_email': User.get_first_super_admin().email
99 },
100 'password_reset_confirmation': {
101 'new_password': 'new-password-example',
102 'user': user,
103 'date': datetime.datetime.now(),
104 'email': 'test@rhodecode.com',
105 'first_admin_email': User.get_first_super_admin().email
106 },
107 'registration': {
108 'user': user,
109 'date': datetime.datetime.now(),
110 },
111
112 'pull_request_comment': {
113 'user': user,
114
115 'status_change': None,
116 'status_change_type': None,
117
118 'pull_request': pr,
119 'pull_request_commits': [],
120
121 'pull_request_target_repo': target_repo,
122 'pull_request_target_repo_url': 'http://target-repo/url',
123
124 'pull_request_source_repo': source_repo,
125 'pull_request_source_repo_url': 'http://source-repo/url',
126
127 'pull_request_url': 'http://localhost/pr1',
128 'pr_comment_url': 'http://comment-url',
129
130 'comment_file': None,
131 'comment_line': None,
132 'comment_type': 'note',
133 'comment_body': 'This is my comment body. *I like !*',
134
135 'renderer_type': 'markdown',
136 'mention': True,
137
138 },
139 'pull_request_comment+status': {
140 'user': user,
141
142 'status_change': 'approved',
143 'status_change_type': 'approved',
144
145 'pull_request': pr,
146 'pull_request_commits': [],
147
148 'pull_request_target_repo': target_repo,
149 'pull_request_target_repo_url': 'http://target-repo/url',
150
151 'pull_request_source_repo': source_repo,
152 'pull_request_source_repo_url': 'http://source-repo/url',
153
154 'pull_request_url': 'http://localhost/pr1',
155 'pr_comment_url': 'http://comment-url',
156
157 'comment_type': 'todo',
158 'comment_file': None,
159 'comment_line': None,
160 'comment_body': '''
161 I think something like this would be better
162
163 ```py
164
165 def db():
166 global connection
167 return connection
168
169 ```
170
171 ''',
172
173 'renderer_type': 'markdown',
174 'mention': True,
175
176 },
177 'pull_request_comment+file': {
178 'user': user,
179
180 'status_change': None,
181 'status_change_type': None,
182
183 'pull_request': pr,
184 'pull_request_commits': [],
185
186 'pull_request_target_repo': target_repo,
187 'pull_request_target_repo_url': 'http://target-repo/url',
188
189 'pull_request_source_repo': source_repo,
190 'pull_request_source_repo_url': 'http://source-repo/url',
191
192 'pull_request_url': 'http://localhost/pr1',
193
194 'pr_comment_url': 'http://comment-url',
195
196 'comment_file': 'rhodecode/model/db.py',
197 'comment_line': 'o1210',
198 'comment_type': 'todo',
199 'comment_body': '''
200 I like this !
201
202 But please check this code::
203
204 def main():
205 print 'ok'
206
207 This should work better !
208 ''',
209
210 'renderer_type': 'rst',
211 'mention': True,
212
213 },
214
215 'cs_comment': {
216 'user': user,
217 'commit': AttributeDict(idx=123, raw_id='a'*40, message='Commit message'),
218 'status_change': None,
219 'status_change_type': None,
220
221 'commit_target_repo_url': 'http://foo.example.com/#comment1',
222 'repo_name': 'test-repo',
223 'comment_type': 'note',
224 'comment_file': None,
225 'comment_line': None,
226 'commit_comment_url': 'http://comment-url',
227 'comment_body': 'This is my comment body. *I like !*',
228 'renderer_type': 'markdown',
229 'mention': True,
230 },
231 'cs_comment+status': {
232 'user': user,
233 'commit': AttributeDict(idx=123, raw_id='a' * 40, message='Commit message'),
234 'status_change': 'approved',
235 'status_change_type': 'approved',
236
237 'commit_target_repo_url': 'http://foo.example.com/#comment1',
238 'repo_name': 'test-repo',
239 'comment_type': 'note',
240 'comment_file': None,
241 'comment_line': None,
242 'commit_comment_url': 'http://comment-url',
243 'comment_body': '''
244 Hello **world**
245
246 This is a multiline comment :)
247
248 - list
249 - list2
250 ''',
251 'renderer_type': 'markdown',
252 'mention': True,
253 },
254 'cs_comment+file': {
255 'user': user,
256 'commit': AttributeDict(idx=123, raw_id='a' * 40, message='Commit message'),
257 'status_change': None,
258 'status_change_type': None,
259
260 'commit_target_repo_url': 'http://foo.example.com/#comment1',
261 'repo_name': 'test-repo',
262
263 'comment_type': 'note',
264 'comment_file': 'test-file.py',
265 'comment_line': 'n100',
266
267 'commit_comment_url': 'http://comment-url',
268 'comment_body': 'This is my comment body. *I like !*',
269 'renderer_type': 'markdown',
270 'mention': True,
271 },
272
273 'pull_request': {
274 'user': user,
275 'pull_request': pr,
276 'pull_request_commits': [
277 ('472d1df03bf7206e278fcedc6ac92b46b01c4e21', '''\
278 my-account: moved email closer to profile as it's similar data just moved outside.
279 '''),
280 ('cbfa3061b6de2696c7161ed15ba5c6a0045f90a7', '''\
281 users: description edit fixes
282
283 - tests
284 - added metatags info
285 '''),
286 ],
287
288 'pull_request_target_repo': target_repo,
289 'pull_request_target_repo_url': 'http://target-repo/url',
290
291 'pull_request_source_repo': source_repo,
292 'pull_request_source_repo_url': 'http://source-repo/url',
293
294 'pull_request_url': 'http://code.rhodecode.com/_pull-request/123',
295 }
296
297 }
298
299 template_type = email_id.split('+')[0]
300 (c.subject, c.headers, c.email_body,
301 c.email_body_plaintext) = EmailNotificationModel().render_email(
302 template_type, **email_kwargs.get(email_id, {}))
303
304 test_email = self.request.GET.get('email')
305 if test_email:
306 recipients = [test_email]
307 run_task(tasks.send_email, recipients, c.subject,
308 c.email_body_plaintext, c.email_body)
309
310 if self.request.matched_route.name == 'debug_style_email_plain_rendered':
311 template = 'debug_style/email_plain_rendered.mako'
312 else:
313 template = 'debug_style/email.mako'
314 return render_to_response(
315 template, self._get_template_context(c),
316 request=self.request)
317
318 @view_config(
49 319 route_name='debug_style_template', request_method='GET',
50 320 renderer=None)
51 321 def template(self):
@@ -53,7 +323,16 b' class DebugStyleView(BaseAppView):'
53 323 c = self.load_default_context()
54 324 c.active = os.path.splitext(t_path)[0]
55 325 c.came_from = ''
326 c.email_types = {
327 'cs_comment+file': {},
328 'cs_comment+status': {},
329
330 'pull_request_comment+file': {},
331 'pull_request_comment+status': {},
332 }
333 c.email_types.update(EmailNotificationModel.email_types)
56 334
57 335 return render_to_response(
58 336 'debug_style/' + t_path, self._get_template_context(c),
59 request=self.request) No newline at end of file
337 request=self.request)
338
@@ -98,7 +98,7 b' class TestRepoCommitCommentsView(TestCon'
98 98 assert notification.type_ == Notification.TYPE_CHANGESET_COMMENT
99 99
100 100 author = notification.created_by_user.username_and_name
101 sbj = '{0} left a {1} on commit `{2}` in the {3} repository'.format(
101 sbj = '@{0} left a {1} on commit `{2}` in the `{3}` repository'.format(
102 102 author, comment_type, h.show_id(commit), backend.repo_name)
103 103 assert sbj == notification.subject
104 104
@@ -159,7 +159,7 b' class TestRepoCommitCommentsView(TestCon'
159 159 assert comment.revision == commit_id
160 160
161 161 author = notification.created_by_user.username_and_name
162 sbj = '{0} left a {1} on file `{2}` in commit `{3}` in the {4} repository'.format(
162 sbj = '@{0} left a {1} on file `{2}` in commit `{3}` in the `{4}` repository'.format(
163 163 author, comment_type, f_path, h.show_id(commit), backend.repo_name)
164 164
165 165 assert sbj == notification.subject
@@ -230,7 +230,7 b' class TestRepoCommitCommentsView(TestCon'
230 230 assert notification.type_ == Notification.TYPE_CHANGESET_COMMENT
231 231
232 232 author = notification.created_by_user.username_and_name
233 sbj = '[status: Approved] {0} left a note on commit `{1}` in the {2} repository'.format(
233 sbj = '[status: Approved] @{0} left a note on commit `{1}` in the `{2}` repository'.format(
234 234 author, h.show_id(commit), backend.repo_name)
235 235 assert sbj == notification.subject
236 236
@@ -467,7 +467,7 b' class TestPullrequestsView(object):'
467 467 .filter(Notification.created_by == pull_request.author.user_id,
468 468 Notification.type_ == Notification.TYPE_PULL_REQUEST,
469 469 Notification.subject.contains(
470 "wants you to review pull request #%s" % pull_request_id))
470 "requested a pull request review. !%s" % pull_request_id))
471 471 assert len(notifications.all()) == 1
472 472
473 473 # Change reviewers and check that a notification was made
@@ -549,11 +549,10 b' class TestPullrequestsView(object):'
549 549 pull_request_id = pull_request.pull_request_id
550 550 repo_name = pull_request.target_repo.scm_instance().name,
551 551
552 response = self.app.post(
553 route_path('pullrequest_merge',
554 repo_name=str(repo_name[0]),
555 pull_request_id=pull_request_id),
556 params={'csrf_token': csrf_token}).follow()
552 url = route_path('pullrequest_merge',
553 repo_name=str(repo_name[0]),
554 pull_request_id=pull_request_id)
555 response = self.app.post(url, params={'csrf_token': csrf_token}).follow()
557 556
558 557 pull_request = PullRequest.get(pull_request_id)
559 558
@@ -735,12 +734,12 b' class TestPullrequestsView(object):'
735 734 backend.pull_heads(source, heads=['change-rebased'])
736 735
737 736 # update PR
738 self.app.post(
739 route_path('pullrequest_update',
740 repo_name=target.repo_name,
741 pull_request_id=pull_request_id),
742 params={'update_commits': 'true', 'csrf_token': csrf_token},
743 status=200)
737 url = route_path('pullrequest_update',
738 repo_name=target.repo_name,
739 pull_request_id=pull_request_id)
740 self.app.post(url,
741 params={'update_commits': 'true', 'csrf_token': csrf_token},
742 status=200)
744 743
745 744 # check that we have now both revisions
746 745 pull_request = PullRequest.get(pull_request_id)
@@ -801,12 +800,12 b' class TestPullrequestsView(object):'
801 800 vcsrepo.run_git_command(['reset', '--soft', 'HEAD~2'])
802 801
803 802 # update PR
804 self.app.post(
805 route_path('pullrequest_update',
806 repo_name=target.repo_name,
807 pull_request_id=pull_request_id),
808 params={'update_commits': 'true', 'csrf_token': csrf_token},
809 status=200)
803 url = route_path('pullrequest_update',
804 repo_name=target.repo_name,
805 pull_request_id=pull_request_id)
806 self.app.post(url,
807 params={'update_commits': 'true', 'csrf_token': csrf_token},
808 status=200)
810 809
811 810 response = self.app.get(route_path('pullrequest_new', repo_name=target.repo_name))
812 811 assert response.status_int == 200
@@ -961,12 +960,12 b' class TestPullrequestsView(object):'
961 960 else:
962 961 vcs.strip(pr_util.commit_ids['new-feature'])
963 962
964 response = self.app.post(
965 route_path('pullrequest_update',
966 repo_name=pull_request.target_repo.repo_name,
967 pull_request_id=pull_request.pull_request_id),
968 params={'update_commits': 'true',
969 'csrf_token': csrf_token})
963 url = route_path('pullrequest_update',
964 repo_name=pull_request.target_repo.repo_name,
965 pull_request_id=pull_request.pull_request_id)
966 response = self.app.post(url,
967 params={'update_commits': 'true',
968 'csrf_token': csrf_token})
970 969
971 970 assert response.status_int == 200
972 971 assert response.body == 'true'
@@ -1208,14 +1207,11 b' class TestPullrequestsControllerDelete(o'
1208 1207
1209 1208
1210 1209 def assert_pull_request_status(pull_request, expected_status):
1211 status = ChangesetStatusModel().calculated_review_status(
1212 pull_request=pull_request)
1210 status = ChangesetStatusModel().calculated_review_status(pull_request=pull_request)
1213 1211 assert status == expected_status
1214 1212
1215 1213
1216 1214 @pytest.mark.parametrize('route', ['pullrequest_new', 'pullrequest_create'])
1217 1215 @pytest.mark.usefixtures("autologin_user")
1218 1216 def test_forbidde_to_repo_summary_for_svn_repositories(backend_svn, app, route):
1219 response = app.get(
1220 route_path(route, repo_name=backend_svn.repo_name), status=404)
1221
1217 app.get(route_path(route, repo_name=backend_svn.repo_name), status=404)
@@ -359,7 +359,7 b' class CommentsModel(BaseModel):'
359 359 kwargs.update({
360 360 'commit': commit_obj,
361 361 'commit_message': commit_obj.message,
362 'commit_target_repo': target_repo_url,
362 'commit_target_repo_url': target_repo_url,
363 363 'commit_comment_url': commit_comment_url,
364 364 })
365 365
@@ -382,6 +382,11 b' class CommentsModel(BaseModel):'
382 382 pull_request_id=pull_request_obj.pull_request_id,
383 383 _anchor='comment-%s' % comment.comment_id)
384 384
385 pr_url = h.route_url(
386 'pullrequest_show',
387 repo_name=pr_target_repo.repo_name,
388 pull_request_id=pull_request_obj.pull_request_id, )
389
385 390 # set some variables for email notification
386 391 pr_target_repo_url = h.route_url(
387 392 'repo_summary', repo_name=pr_target_repo.repo_name)
@@ -393,10 +398,11 b' class CommentsModel(BaseModel):'
393 398 kwargs.update({
394 399 'pull_request': pull_request_obj,
395 400 'pr_id': pull_request_obj.pull_request_id,
396 'pr_target_repo': pr_target_repo,
397 'pr_target_repo_url': pr_target_repo_url,
398 'pr_source_repo': pr_source_repo,
399 'pr_source_repo_url': pr_source_repo_url,
401 'pull_request_url': pr_url,
402 'pull_request_target_repo': pr_target_repo,
403 'pull_request_target_repo_url': pr_target_repo_url,
404 'pull_request_source_repo': pr_source_repo,
405 'pull_request_source_repo_url': pr_source_repo_url,
400 406 'pr_comment_url': pr_comment_url,
401 407 'pr_closing': closing_pr,
402 408 })
@@ -1110,8 +1110,8 b' class PullRequestModel(BaseModel):'
1110 1110 pr_target_repo = pull_request_obj.target_repo
1111 1111
1112 1112 pr_url = h.route_url('pullrequest_show',
1113 repo_name=pr_target_repo.repo_name,
1114 pull_request_id=pull_request_obj.pull_request_id,)
1113 repo_name=pr_target_repo.repo_name,
1114 pull_request_id=pull_request_obj.pull_request_id,)
1115 1115
1116 1116 # set some variables for email notification
1117 1117 pr_target_repo_url = h.route_url(
@@ -612,7 +612,8 b' class UserModel(BaseModel):'
612 612 'password_reset_url': pwd_reset_url,
613 613 'user': user,
614 614 'email': user_email,
615 'date': datetime.datetime.now()
615 'date': datetime.datetime.now(),
616 'first_admin_email': User.get_first_super_admin().email
616 617 }
617 618
618 619 (subject, headers, email_body,
@@ -670,7 +671,8 b' class UserModel(BaseModel):'
670 671 'new_password': new_passwd,
671 672 'user': user,
672 673 'email': user_email,
673 'date': datetime.datetime.now()
674 'date': datetime.datetime.now(),
675 'first_admin_email': User.get_first_super_admin().email
674 676 }
675 677
676 678 (subject, headers, email_body,
@@ -342,6 +342,8 b' function registerRCRoutes() {'
342 342 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
343 343 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
344 344 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
345 pyroutes.register('debug_style_email', '/_admin/debug_style/email/%(email_id)s', ['email_id']);
346 pyroutes.register('debug_style_email_plain_rendered', '/_admin/debug_style/email-rendered/%(email_id)s', ['email_id']);
345 347 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
346 348 pyroutes.register('apiv2', '/_admin/api', []);
347 349 pyroutes.register('admin_settings_license', '/_admin/settings/license', []);
@@ -52,6 +52,7 b''
52 52 <div class="sidebar">
53 53 <ul class="nav nav-pills nav-stacked">
54 54 <li class="${'active' if c.active=='index' else ''}"><a href="${h.route_path('debug_style_home')}">${_('Index')}</a></li>
55 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('debug_style_template', t_path='emails.html')}">${_('Emails')}</a></li>
55 56 <li class="${'active' if c.active=='typography' else ''}"><a href="${h.route_path('debug_style_template', t_path='typography.html')}">${_('Typography')}</a></li>
56 57 <li class="${'active' if c.active=='forms' else ''}"><a href="${h.route_path('debug_style_template', t_path='forms.html')}">${_('Forms')}</a></li>
57 58 <li class="${'active' if c.active=='buttons' else ''}"><a href="${h.route_path('debug_style_template', t_path='buttons.html')}">${_('Buttons')}</a></li>
@@ -2,15 +2,23 b''
2 2
3 3 ## helpers
4 4 <%def name="tag_button(text, tag_type=None)">
5 <%
6 color_scheme = {
7 'default': 'border:1px solid #979797;color:#666666;background-color:#f9f9f9',
8 'approved': 'border:1px solid #0ac878;color:#0ac878;background-color:#f9f9f9',
9 'rejected': 'border:1px solid #e85e4d;color:#e85e4d;background-color:#f9f9f9',
10 'under_review': 'border:1px solid #ffc854;color:#ffc854;background-color:#f9f9f9',
11 }
12 %>
13 <pre style="display:inline;border-radius:2px;font-size:12px;padding:.2em;${color_scheme.get(tag_type, color_scheme['default'])}">${text}</pre>
5 <%
6 color_scheme = {
7 'default': 'border:1px solid #979797;color:#666666;background-color:#f9f9f9',
8 'approved': 'border:1px solid #0ac878;color:#0ac878;background-color:#f9f9f9',
9 'rejected': 'border:1px solid #e85e4d;color:#e85e4d;background-color:#f9f9f9',
10 'under_review': 'border:1px solid #ffc854;color:#ffc854;background-color:#f9f9f9',
11 }
12
13 css_style = ';'.join([
14 'display:inline',
15 'border-radius:2px',
16 'font-size:12px',
17 'padding:.2em',
18 ])
19
20 %>
21 <pre style="${css_style}; ${color_scheme.get(tag_type, color_scheme['default'])}">${text}</pre>
14 22 </%def>
15 23
16 24 <%def name="status_text(text, tag_type=None)">
@@ -25,6 +33,34 b''
25 33 <span style="font-weight:bold;font-size:12px;padding:.2em;${color_scheme.get(tag_type, color_scheme['default'])}">${text}</span>
26 34 </%def>
27 35
36 <%def name="gravatar_img(email, size=16)">
37 <%
38 css_style = ';'.join([
39 'padding: 0',
40 'margin: -4px 0',
41 'border-radius: 50%',
42 'box-sizing: content-box',
43 'display: inline',
44 'line-height: 1em',
45 'min-width: 16px',
46 'min-height: 16px',
47 ])
48 %>
49
50 <img alt="gravatar" style="${css_style}" src="${h.gravatar_url(email, size)}" height="${size}" width="${size}">
51 </%def>
52
53 <%def name="link_css()">\
54 <%
55 css_style = ';'.join([
56 'color:#427cc9',
57 'text-decoration:none',
58 'cursor:pointer'
59 ])
60 %>\
61 ${css_style}\
62 </%def>
63
28 64 ## Constants
29 65 <%
30 66 text_regular = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;"
@@ -35,8 +71,8 b' text_monospace = "\'Menlo\', \'Liberation M'
35 71 ## headers we additionally can set for email
36 72 <%def name="headers()" filter="n,trim"></%def>
37 73
38 <%def name="plaintext_footer()">
39 ${_('This is a notification from RhodeCode. %(instance_url)s') % {'instance_url': instance_url}}
74 <%def name="plaintext_footer()" filter="trim">
75 ${_('This is a notification from RhodeCode.')} ${instance_url}
40 76 </%def>
41 77
42 78 <%def name="body_plaintext()" filter="n,trim">
@@ -52,36 +88,122 b' text_monospace = "\'Menlo\', \'Liberation M'
52 88 <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
53 89 <title>${self.subject()}</title>
54 90 <style type="text/css">
55 /* Based on The MailChimp Reset INLINE: Yes. */
56 #outlook a {padding:0;} /* Force Outlook to provide a "view in browser" menu link. */
57 body{width:100% !important; -webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; margin:0; padding:0; font-family: ${text_regular|n}}
58 /* Prevent Webkit and Windows Mobile platforms from changing default font sizes.*/
59 .ExternalClass {width:100%;} /* Force Hotmail to display emails at full width */
60 .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;}
61 /* Forces Hotmail to display normal line spacing. More on that: http://www.emailonacid.com/forum/viewthread/43/ */
62 #backgroundTable {margin:0; padding:0; line-height: 100% !important;}
91 /* Based on The MailChimp Reset INLINE: Yes. */
92 #outlook a {
93 padding: 0;
94 }
95
96 /* Force Outlook to provide a "view in browser" menu link. */
97 body {
98 width: 100% !important;
99 -webkit-text-size-adjust: 100%;
100 -ms-text-size-adjust: 100%;
101 margin: 0;
102 padding: 0;
103 font-family: ${text_regular|n}
104 }
105
106 /* Prevent Webkit and Windows Mobile platforms from changing default font sizes.*/
107 .ExternalClass {
108 width: 100%;
109 }
110
111 /* Force Hotmail to display emails at full width */
112 .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {
113 line-height: 100%;
114 }
115
116 /* Forces Hotmail to display normal line spacing. More on that: http://www.emailonacid.com/forum/viewthread/43/ */
117 #backgroundTable {
118 margin: 0;
119 padding: 0;
120 line-height: 100% !important;
121 }
122
63 123 /* End reset */
64 124
65 125 /* defaults for images*/
66 img {outline:none; text-decoration:none; -ms-interpolation-mode: bicubic;}
67 a img {border:none;}
68 .image_fix {display:block;}
126 img {
127 outline: none;
128 text-decoration: none;
129 -ms-interpolation-mode: bicubic;
130 }
131
132 a img {
133 border: none;
134 }
135
136 .image_fix {
137 display: block;
138 }
139
140 body {
141 line-height: 1.2em;
142 }
143
144 p {
145 margin: 0 0 20px;
146 }
147
148 h1, h2, h3, h4, h5, h6 {
149 color: #323232 !important;
150 }
151
152 a {
153 color: #427cc9;
154 text-decoration: none;
155 outline: none;
156 cursor: pointer;
157 }
158
159 a:focus {
160 outline: none;
161 }
162
163 a:hover {
164 color: #305b91;
165 }
69 166
70 body {line-height:1.2em;}
71 p {margin: 0 0 20px;}
72 h1, h2, h3, h4, h5, h6 {color:#323232!important;}
73 a {color:#427cc9;text-decoration:none;outline:none;cursor:pointer;}
74 a:focus {outline:none;}
75 a:hover {color: #305b91;}
76 h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {color:#427cc9!important;text-decoration:none!important;}
77 h1 a:active, h2 a:active, h3 a:active, h4 a:active, h5 a:active, h6 a:active {color: #305b91!important;}
78 h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited {color: #305b91!important;}
79 table {font-size:13px;border-collapse:collapse;mso-table-lspace:0pt;mso-table-rspace:0pt;}
80 table td {padding:.65em 1em .65em 0;border-collapse:collapse;vertical-align:top;text-align:left;}
81 input {display:inline;border-radius:2px;border-style:solid;border: 1px solid #dbd9da;padding:.5em;}
82 input:focus {outline: 1px solid #979797}
167 h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
168 color: #427cc9 !important;
169 text-decoration: none !important;
170 }
171
172 h1 a:active, h2 a:active, h3 a:active, h4 a:active, h5 a:active, h6 a:active {
173 color: #305b91 !important;
174 }
175
176 h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited {
177 color: #305b91 !important;
178 }
179
180 table {
181 font-size: 13px;
182 border-collapse: collapse;
183 mso-table-lspace: 0pt;
184 mso-table-rspace: 0pt;
185 }
186
187 table td {
188 padding: .65em 1em .65em 0;
189 border-collapse: collapse;
190 vertical-align: top;
191 text-align: left;
192 }
193
194 input {
195 display: inline;
196 border-radius: 2px;
197 border: 1px solid #dbd9da;
198 padding: .5em;
199 }
200
201 input:focus {
202 outline: 1px solid #979797
203 }
204
83 205 @media only screen and (-webkit-min-device-pixel-ratio: 2) {
84 /* Put your iPhone 4g styles in here */
206 /* Put your iPhone 4g styles in here */
85 207 }
86 208
87 209 /* Android targeting */
@@ -96,6 +218,262 b' text_monospace = "\'Menlo\', \'Liberation M'
96 218 }
97 219 /* end Android targeting */
98 220
221 /** MARKDOWN styling **/
222 div.markdown-block {
223 clear: both;
224 overflow: hidden;
225 margin: 0;
226 padding: 3px 5px 3px
227 }
228
229 div.markdown-block h1, div.markdown-block h2, div.markdown-block h3, div.markdown-block h4, div.markdown-block h5, div.markdown-block h6 {
230 border-bottom: none !important;
231 padding: 0 !important;
232 overflow: visible !important
233 }
234
235 div.markdown-block h1, div.markdown-block h2 {
236 border-bottom: 1px #e6e5e5 solid !important
237 }
238
239 div.markdown-block h1 {
240 font-size: 32px;
241 margin: 15px 0 15px 0 !important;
242 padding-bottom: 5px !important
243 }
244
245 div.markdown-block h2 {
246 font-size: 24px !important;
247 margin: 34px 0 10px 0 !important;
248 padding-top: 15px !important;
249 padding-bottom: 8px !important
250 }
251
252 div.markdown-block h3 {
253 font-size: 18px !important;
254 margin: 30px 0 8px 0 !important;
255 padding-bottom: 2px !important
256 }
257
258 div.markdown-block h4 {
259 font-size: 13px !important;
260 margin: 18px 0 3px 0 !important
261 }
262
263 div.markdown-block h5 {
264 font-size: 12px !important;
265 margin: 15px 0 3px 0 !important
266 }
267
268 div.markdown-block h6 {
269 font-size: 12px;
270 color: #777777;
271 margin: 15px 0 3px 0 !important
272 }
273
274 div.markdown-block hr {
275 border: 0;
276 color: #e6e5e5;
277 background-color: #e6e5e5;
278 height: 3px;
279 margin-bottom: 13px
280 }
281
282 div.markdown-block ol, div.markdown-block ul, div.markdown-block p, div.markdown-block blockquote, div.markdown-block dl, div.markdown-block li, div.markdown-block table {
283 margin: 3px 0 13px 0 !important;
284 color: #424242 !important;
285 font-size: 13px !important;
286 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
287 font-weight: normal !important;
288 overflow: visible !important;
289 line-height: 140% !important
290 }
291
292 div.markdown-block pre {
293 margin: 3px 0 13px 0 !important;
294 padding: .5em;
295 color: #424242 !important;
296 font-size: 13px !important;
297 overflow: visible !important;
298 line-height: 140% !important;
299 background-color: #F5F5F5
300 }
301
302 div.markdown-block img {
303 border-style: none;
304 background-color: #fff;
305 padding-right: 20px;
306 max-width: 100%
307 }
308
309 div.markdown-block strong {
310 font-weight: 600;
311 margin: 0
312 }
313
314 div.markdown-block ul.checkbox, div.markdown-block ol.checkbox {
315 padding-left: 20px !important;
316 margin-top: 0 !important;
317 margin-bottom: 18px !important
318 }
319
320 div.markdown-block ul, div.markdown-block ol {
321 padding-left: 30px !important;
322 margin-top: 0 !important;
323 margin-bottom: 18px !important
324 }
325
326 div.markdown-block ul.checkbox li, div.markdown-block ol.checkbox li {
327 list-style: none !important;
328 margin: 6px !important;
329 padding: 0 !important
330 }
331
332 div.markdown-block ul li, div.markdown-block ol li {
333 list-style: disc !important;
334 margin: 6px !important;
335 padding: 0 !important
336 }
337
338 div.markdown-block ol li {
339 list-style: decimal !important
340 }
341
342 div.markdown-block #message {
343 -webkit-border-radius: 2px;
344 -moz-border-radius: 2px;
345 border-radius: 2px;
346 border: 1px solid #dbd9da;
347 display: block;
348 width: 100%;
349 height: 60px;
350 margin: 6px 0
351 }
352
353 div.markdown-block button, div.markdown-block #ws {
354 font-size: 13px;
355 padding: 4px 6px;
356 -webkit-border-radius: 2px;
357 -moz-border-radius: 2px;
358 border-radius: 2px;
359 border: 1px solid #dbd9da;
360 background-color: #eeeeee
361 }
362
363 div.markdown-block code, div.markdown-block pre, div.markdown-block #ws, div.markdown-block #message {
364 font-family: 'Menlo', 'Liberation Mono', 'Consolas', 'DejaVu Sans Mono', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace;
365 font-size: 11px;
366 -webkit-border-radius: 2px;
367 -moz-border-radius: 2px;
368 border-radius: 2px;
369 background-color: white;
370 color: #7E7F7F
371 }
372
373 div.markdown-block code {
374 border: 1px solid #eeeeee;
375 margin: 0 2px;
376 padding: 0 5px
377 }
378
379 div.markdown-block pre {
380 border: 1px solid #dbd9da;
381 overflow: auto;
382 padding: .5em;
383 background-color: #F5F5F5
384 }
385
386 div.markdown-block pre > code {
387 border: 0;
388 margin: 0;
389 padding: 0
390 }
391
392 div.rst-block {
393 clear: both;
394 overflow: hidden;
395 margin: 0;
396 padding: 3px 5px 3px
397 }
398
399 div.rst-block h2 {
400 font-weight: normal
401 }
402
403 div.rst-block h1, div.rst-block h2, div.rst-block h3, div.rst-block h4, div.rst-block h5, div.rst-block h6 {
404 border-bottom: 0 !important;
405 margin: 0 !important;
406 padding: 0 !important;
407 line-height: 1.5em !important
408 }
409
410 div.rst-block h1:first-child {
411 padding-top: .25em !important
412 }
413
414 div.rst-block h2, div.rst-block h3 {
415 margin: 1em 0 !important
416 }
417
418 div.rst-block h1, div.rst-block h2 {
419 border-bottom: 1px #e6e5e5 solid !important
420 }
421
422 div.rst-block h2 {
423 margin-top: 1.5em !important;
424 padding-top: .5em !important
425 }
426
427 div.rst-block p {
428 color: black !important;
429 margin: 1em 0 !important;
430 line-height: 1.5em !important
431 }
432
433 div.rst-block ul {
434 list-style: disc !important;
435 margin: 1em 0 1em 2em !important;
436 clear: both
437 }
438
439 div.rst-block ol {
440 list-style: decimal;
441 margin: 1em 0 1em 2em !important
442 }
443
444 div.rst-block pre, div.rst-block code {
445 font: 12px "Bitstream Vera Sans Mono", "Courier", monospace
446 }
447
448 div.rst-block code {
449 font-size: 12px !important;
450 background-color: ghostWhite !important;
451 color: #444 !important;
452 padding: 0 .2em !important;
453 border: 1px solid #dedede !important
454 }
455
456 div.rst-block pre code {
457 padding: 0 !important;
458 font-size: 12px !important;
459 background-color: #eee !important;
460 border: none !important
461 }
462
463 div.rst-block pre {
464 margin: 1em 0;
465 padding: 15px;
466 border: 1px solid #eeeeee;
467 -webkit-border-radius: 2px;
468 -moz-border-radius: 2px;
469 border-radius: 2px;
470 overflow: auto;
471 font-size: 12px;
472 color: #444;
473 background-color: #F5F5F5
474 }
475
476
99 477 </style>
100 478
101 479 <!-- Targeting Windows Mobile -->
@@ -106,26 +484,30 b' text_monospace = "\'Menlo\', \'Liberation M'
106 484 <![endif]-->
107 485
108 486 <!--[if gte mso 9]>
109 <style>
110 /* Target Outlook 2007 and 2010 */
111 </style>
487 <style>
488 /* Target Outlook 2007 and 2010 */
489 </style>
112 490 <![endif]-->
113 491 </head>
114 492 <body>
115 493 <!-- 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. -->
116 494 <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">
117 495 <tr>
118 <td valign="top" style="padding:0;">
496 <td valign="top" style="padding:0;">
119 497 <table cellpadding="0" cellspacing="0" border="0" align="left" width="100%">
120 <tr><td style="width:100%;padding:7px;background-color:#202020" valign="top">
121 <a style="color:#eeeeee;text-decoration:none;" href="${instance_url}">
122 ${_('RhodeCode')}
123 % if rhodecode_instance_name:
124 - ${rhodecode_instance_name}
125 % endif
126 </a>
127 </td></tr>
128 <tr><td style="padding:15px;" valign="top">${self.body()}</td></tr>
498 <tr>
499 <td style="width:100%;padding:10px 15px;background-color:#202020" valign="top">
500 <a style="color:#eeeeee;text-decoration:none;" href="${instance_url}">
501 ${_('RhodeCode')}
502 % if rhodecode_instance_name:
503 - ${rhodecode_instance_name}
504 % endif
505 </a>
506 </td>
507 </tr>
508 <tr>
509 <td style="padding:15px;" valign="top">${self.body()}</td>
510 </tr>
129 511 </table>
130 512 </td>
131 513 </tr>
@@ -133,10 +515,11 b' text_monospace = "\'Menlo\', \'Liberation M'
133 515 <!-- End of wrapper table -->
134 516
135 517 <div style="clear: both"></div>
136 <p>
137 <a style="margin-top:15px;margin-left:1%;font-weight:100;font-size:11px;color:#666666;text-decoration:none;font-family:${text_monospace} " href="${instance_url}">
138 ${self.plaintext_footer()}
139 </a>
140 </p>
518 <div style="margin-left:1%;font-weight:100;font-size:11px;color:#666666;text-decoration:none;font-family:${text_monospace}">
519 ${_('This is a notification from RhodeCode.')}
520 <a style="font-weight:100;font-size:11px;color:#666666;text-decoration:none;font-family:${text_monospace}" href="${instance_url}">
521 ${instance_url}
522 </a>
523 </div>
141 524 </body>
142 525 </html>
@@ -6,24 +6,25 b''
6 6 <%def name="subject()" filter="n,trim,whitespace_filter">
7 7 <%
8 8 data = {
9 'user': h.person(user),
9 'user': '@'+h.person(user),
10 10 'repo_name': repo_name,
11 'commit_id': h.show_id(commit),
12 11 'status': status_change,
13 12 'comment_file': comment_file,
14 13 'comment_line': comment_line,
15 14 'comment_type': comment_type,
15
16 'commit_id': h.show_id(commit),
16 17 }
17 18 %>
18 ${_('[mention]') if mention else ''} \
19
19 20
20 21 % if comment_file:
21 ${_('{user} left a {comment_type} on file `{comment_file}` in commit `{commit_id}`').format(**data)} ${_('in the {repo_name} repository').format(**data) |n}
22 ${(_('[mention]') if mention else '')} ${_('{user} left a {comment_type} on file `{comment_file}` in commit `{commit_id}`').format(**data)} ${_('in the `{repo_name}` repository').format(**data) |n}
22 23 % else:
23 24 % if status_change:
24 ${_('[status: {status}] {user} left a {comment_type} on commit `{commit_id}`').format(**data) |n} ${_('in the {repo_name} repository').format(**data) |n}
25 ${(_('[mention]') if mention else '')} ${_('[status: {status}] {user} left a {comment_type} on commit `{commit_id}`').format(**data) |n} ${_('in the `{repo_name}` repository').format(**data) |n}
25 26 % else:
26 ${_('{user} left a {comment_type} on commit `{commit_id}`').format(**data) |n} ${_('in the {repo_name} repository').format(**data) |n}
27 ${(_('[mention]') if mention else '')} ${_('{user} left a {comment_type} on commit `{commit_id}`').format(**data) |n} ${_('in the `{repo_name}` repository').format(**data) |n}
27 28 % endif
28 29 % endif
29 30
@@ -35,31 +36,38 b' data = {'
35 36 data = {
36 37 'user': h.person(user),
37 38 'repo_name': repo_name,
38 'commit_id': h.show_id(commit),
39 39 'status': status_change,
40 40 'comment_file': comment_file,
41 41 'comment_line': comment_line,
42 42 'comment_type': comment_type,
43
44 'commit_id': h.show_id(commit),
43 45 }
44 46 %>
45 ${self.subject()}
46 47
47 48 * ${_('Comment link')}: ${commit_comment_url}
48 49
50 %if status_change:
51 * ${_('Commit status')}: ${_('Status was changed to')}: *${status_change}*
52
53 %endif
49 54 * ${_('Commit')}: ${h.show_id(commit)}
50 55
56 * ${_('Commit message')}: ${commit.message}
57
51 58 %if comment_file:
52 59 * ${_('File: {comment_file} on line {comment_line}').format(**data)}
60
53 61 %endif
62 % if comment_type == 'todo':
63 ${_('`TODO` comment')}:
64 % else:
65 ${_('`Note` comment')}:
66 % endif
67
68 ${comment_body |n, trim}
54 69
55 70 ---
56
57 %if status_change:
58 ${_('Commit status was changed to')}: *${status_change}*
59 %endif
60
61 ${comment_body|n}
62
63 71 ${self.plaintext_footer()}
64 72 </%def>
65 73
@@ -67,42 +75,87 b' data = {'
67 75 <%
68 76 data = {
69 77 'user': h.person(user),
70 'repo': commit_target_repo,
71 'repo_name': repo_name,
72 'commit_id': h.show_id(commit),
73 78 'comment_file': comment_file,
74 79 'comment_line': comment_line,
75 80 'comment_type': comment_type,
81 'renderer_type': renderer_type or 'plain',
82
83 'repo': commit_target_repo_url,
84 'repo_name': repo_name,
85 'commit_id': h.show_id(commit),
76 86 }
77 87 %>
78 <table style="text-align:left;vertical-align:middle;">
79 <tr><td colspan="2" style="width:100%;padding-bottom:15px;border-bottom:1px solid #dbd9da;">
88
89 <table style="text-align:left;vertical-align:middle;width: 100%">
90 <tr>
91 <td style="width:100%;border-bottom:1px solid #dbd9da;">
80 92
81 % if comment_file:
82 <h4><a href="${commit_comment_url}" style="color:#427cc9;text-decoration:none;cursor:pointer">${_('{user} left a {comment_type} on file `{comment_file}` in commit `{commit_id}`').format(**data)}</a> ${_('in the {repo} repository').format(**data) |n}</h4>
83 % else:
84 <h4><a href="${commit_comment_url}" style="color:#427cc9;text-decoration:none;cursor:pointer">${_('{user} left a {comment_type} on commit `{commit_id}`').format(**data) |n}</a> ${_('in the {repo} repository').format(**data) |n}</h4>
85 % endif
86 </td></tr>
93 <h4 style="margin: 0">
94 <div style="margin-bottom: 4px; color:#7E7F7F">
95 @${h.person(user.username)}
96 </div>
97 ${_('left a')}
98 <a href="${commit_comment_url}" style="${base.link_css()}">
99 % if comment_file:
100 ${_('{comment_type} on file `{comment_file}` in commit.').format(**data)}
101 % else:
102 ${_('{comment_type} on commit.').format(**data) |n}
103 % endif
104 </a>
105 <div style="margin-top: 10px"></div>
106 ${_('Commit')} <code>${data['commit_id']}</code> ${_('of repository')}: ${data['repo_name']}
107 </h4>
87 108
88 <tr><td style="padding-right:20px;padding-top:15px;">${_('Commit')}</td><td style="padding-top:15px;"><a href="${commit_comment_url}" style="color:#427cc9;text-decoration:none;cursor:pointer">${h.show_id(commit)}</a></td></tr>
89 <tr><td style="padding-right:20px;">${_('Description')}</td><td style="white-space:pre-wrap">${h.urlify_commit_message(commit.message, repo_name)}</td></tr>
109 </td>
110 </tr>
111
112 </table>
113
114 <table style="text-align:left;vertical-align:middle;width: 100%">
115
116 ## spacing def
117 <tr>
118 <td style="width: 130px"></td>
119 <td></td>
120 </tr>
90 121
91 122 % if status_change:
92 123 <tr>
93 <td style="padding-right:20px;">${_('Status')}</td>
124 <td style="padding-right:20px;">${_('Commit Status')}:</td>
94 125 <td>
95 ${_('The commit status was changed to')}: ${base.status_text(status_change, tag_type=status_change_type)}
126 ${_('Status was changed to')}: ${base.status_text(status_change, tag_type=status_change_type)}
96 127 </td>
97 128 </tr>
98 129 % endif
130
99 131 <tr>
100 <td style="padding-right:20px;">
132 <td style="padding-right:20px;">${_('Commit')}:</td>
133 <td>
134 <a href="${commit_comment_url}" style="${base.link_css()}">${h.show_id(commit)}</a>
135 </td>
136 </tr>
137 <tr>
138 <td style="padding-right:20px;">${_('Commit message')}:</td>
139 <td style="white-space:pre-wrap">${h.urlify_commit_message(commit.message, repo_name)}</td>
140 </tr>
141
142 % if comment_file:
143 <tr>
144 <td style="padding-right:20px;">${_('File')}:</td>
145 <td><a href="${commit_comment_url}" style="${base.link_css()}">${_('`{comment_file}` on line {comment_line}').format(**data)}</a></td>
146 </tr>
147 % endif
148
149 <tr style="background-image: linear-gradient(to right, black 33%, rgba(255,255,255,0) 0%);background-position: bottom;background-size: 3px 1px;background-repeat: repeat-x;">
150 <td colspan="2" style="padding-right:20px;">
101 151 % if comment_type == 'todo':
102 ${(_('TODO comment on line: {comment_line}') if comment_file else _('TODO comment')).format(**data)}
152 ${_('`TODO` comment')}:
103 153 % else:
104 ${(_('Note comment on line: {comment_line}') if comment_file else _('Note comment')).format(**data)}
154 ${_('`Note` comment')}:
105 155 % endif
106 156 </td>
107 <td style="line-height:1.2em;white-space:pre-wrap">${h.render(comment_body, renderer=renderer_type, mentions=True)}</td></tr>
157 </tr>
158
159 <td colspan="2" style="background: #F7F7F7">${h.render(comment_body, renderer=data['renderer_type'], mentions=True)}</td>
160 </tr>
108 161 </table>
@@ -1,5 +1,6 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base.mako"/>
3 <%namespace name="base" file="base.mako"/>
3 4
4 5 <%def name="subject()" filter="n,trim,whitespace_filter">
5 6 RhodeCode test email: ${h.format_date(date)}
@@ -7,7 +8,13 b' RhodeCode test email: ${h.format_date(da'
7 8
8 9 ## plain text version of the email. Empty by default
9 10 <%def name="body_plaintext()" filter="n,trim">
10 Test Email from RhodeCode version: ${rhodecode_version}, sent by: ${user}
11 Test Email from RhodeCode version: ${rhodecode_version}
12 Email sent by: ${h.person(user)}
13
14 ---
15 ${self.plaintext_footer()}
11 16 </%def>
12 17
13 ${body_plaintext()} No newline at end of file
18 Test Email from RhodeCode version: ${rhodecode_version}
19 <br/><br/>
20 Email sent by: <strong>${h.person(user)}</strong>
@@ -9,13 +9,13 b''
9 9 <%def name="body_plaintext()" filter="n,trim">
10 10 ${body}
11 11
12 ---
12 13 ${self.plaintext_footer()}
13 14 </%def>
14 15
15 16 ## BODY GOES BELOW
16 17 <table style="text-align:left;vertical-align:top;">
17 <tr><td style="padding-right:20px;padding-top:15px;white-space:pre-wrap">${body}</td></tr>
18 <tr>
19 <td style="padding-right:20px;padding-top:15px;white-space:pre-wrap">${body}</td>
20 </tr>
18 21 </table>
19 <p><a style="margin-top:15px;margin-left:1%;font-family:sans-serif;font-weight:100;font-size:11px;display:block;color:#666666;text-decoration:none;" href="${instance_url}">
20 ${self.plaintext_footer()}
21 </a></p> No newline at end of file
@@ -1,5 +1,6 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base.mako"/>
3 <%namespace name="base" file="base.mako"/>
3 4
4 5 <%def name="subject()" filter="n,trim,whitespace_filter">
5 6 RhodeCode Password reset
@@ -7,16 +8,18 b' RhodeCode Password reset'
7 8
8 9 ## plain text version of the email. Empty by default
9 10 <%def name="body_plaintext()" filter="n,trim">
10 Hi ${user.username},
11 Hello ${user.username},
11 12
12 There was a request to reset your password using the email address ${email} on ${h.format_date(date)}
13 On ${h.format_date(date)} there was a request to reset your password using the email address `${email}`
13 14
14 *If you didn't do this, please contact your RhodeCode administrator.*
15 *If you did not request a password reset, please contact your RhodeCode administrator at: ${first_admin_email}*
15 16
16 17 You can continue, and generate new password by clicking following URL:
17 18 ${password_reset_url}
18 19
19 20 This link will be active for 10 minutes.
21
22 ---
20 23 ${self.plaintext_footer()}
21 24 </%def>
22 25
@@ -24,10 +27,11 b' This link will be active for 10 minutes.'
24 27 <p>
25 28 Hello ${user.username},
26 29 </p><p>
27 There was a request to reset your password using the email address ${email} on ${h.format_date(date)}
28 <br/>
29 <strong>If you did not request a password reset, please contact your RhodeCode administrator.</strong>
30 On ${h.format_date(date)} there was a request to reset your password using the email address `${email}`
31 <br/><br/>
32 <strong>If you did not request a password reset, please contact your RhodeCode administrator at: ${first_admin_email}.</strong>
30 33 </p><p>
31 <a href="${password_reset_url}">${_('Generate new password here')}.</a>
32 This link will be active for 10 minutes.
34 You can continue, and generate new password by clicking following URL:<br/><br/>
35 <a href="${password_reset_url}" style="${base.link_css()}">${password_reset_url}</a>
36 <br/><br/>This link will be active for 10 minutes.
33 37 </p>
@@ -1,5 +1,6 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base.mako"/>
3 <%namespace name="base" file="base.mako"/>
3 4
4 5 <%def name="subject()" filter="n,trim,whitespace_filter">
5 6 Your new RhodeCode password
@@ -7,14 +8,15 b' Your new RhodeCode password'
7 8
8 9 ## plain text version of the email. Empty by default
9 10 <%def name="body_plaintext()" filter="n,trim">
10 Hi ${user.username},
11 Hello ${user.username},
11 12
12 Below is your new access password for RhodeCode.
13 Below is your new access password for RhodeCode requested via password reset link.
13 14
14 *If you didn't do this, please contact your RhodeCode administrator.*
15 *If you did not request a password reset, please contact your RhodeCode administrator at: ${first_admin_email}.*
15 16
16 password: ${new_password}
17 new password: ${new_password}
17 18
19 ---
18 20 ${self.plaintext_footer()}
19 21 </%def>
20 22
@@ -22,8 +24,8 b' password: ${new_password}'
22 24 <p>
23 25 Hello ${user.username},
24 26 </p><p>
25 Below is your new access password for RhodeCode.
26 <br/>
27 <strong>If you didn't request a new password, please contact your RhodeCode administrator.</strong>
27 Below is your new access password for RhodeCode requested via password reset link.
28 <br/><br/>
29 <strong>If you did not request a password reset, please contact your RhodeCode administrator at: ${first_admin_email}.</strong>
28 30 </p>
29 <p>password: <pre>${new_password}</pre>
31 <p>new password: <code>${new_password}</code>
@@ -6,27 +6,29 b''
6 6 <%def name="subject()" filter="n,trim,whitespace_filter">
7 7 <%
8 8 data = {
9 'user': h.person(user),
10 'pr_title': pull_request.title,
11 'pr_id': pull_request.pull_request_id,
9 'user': '@'+h.person(user),
10 'repo_name': repo_name,
12 11 'status': status_change,
13 12 'comment_file': comment_file,
14 13 'comment_line': comment_line,
15 14 'comment_type': comment_type,
15
16 'pr_title': pull_request.title,
17 'pr_id': pull_request.pull_request_id,
16 18 }
17 19 %>
18 20
19 ${(_('[mention]') if mention else '')} \
20 21
21 22 % if comment_file:
22 ${_('{user} left a {comment_type} on file `{comment_file}` in pull request #{pr_id} "{pr_title}"').format(**data) |n}
23 ${(_('[mention]') if mention else '')} ${_('{user} left a {comment_type} on file `{comment_file}` in pull request !{pr_id}: "{pr_title}"').format(**data) |n}
23 24 % else:
24 25 % if status_change:
25 ${_('[status: {status}] {user} left a {comment_type} on pull request #{pr_id} "{pr_title}"').format(**data) |n}
26 ${(_('[mention]') if mention else '')} ${_('[status: {status}] {user} left a {comment_type} on pull request !{pr_id}: "{pr_title}"').format(**data) |n}
26 27 % else:
27 ${_('{user} left a {comment_type} on pull request #{pr_id} "{pr_title}"').format(**data) |n}
28 ${(_('[mention]') if mention else '')} ${_('{user} left a {comment_type} on pull request !{pr_id}: "{pr_title}"').format(**data) |n}
28 29 % endif
29 30 % endif
31
30 32 </%def>
31 33
32 34 ## PLAINTEXT VERSION OF BODY
@@ -34,34 +36,51 b' data = {'
34 36 <%
35 37 data = {
36 38 'user': h.person(user),
37 'pr_title': pull_request.title,
38 'pr_id': pull_request.pull_request_id,
39 'repo_name': repo_name,
39 40 'status': status_change,
40 41 'comment_file': comment_file,
41 42 'comment_line': comment_line,
42 43 'comment_type': comment_type,
44
45 'pr_title': pull_request.title,
46 'pr_id': pull_request.pull_request_id,
47 'source_ref_type': pull_request.source_ref_parts.type,
48 'source_ref_name': pull_request.source_ref_parts.name,
49 'target_ref_type': pull_request.target_ref_parts.type,
50 'target_ref_name': pull_request.target_ref_parts.name,
51 'source_repo': pull_request_source_repo.repo_name,
52 'target_repo': pull_request_target_repo.repo_name,
53 'source_repo_url': pull_request_source_repo_url,
54 'target_repo_url': pull_request_target_repo_url,
43 55 }
44 56 %>
45 ${self.subject()}
57
58 ${h.literal(_('Pull request !{pr_id}: `{pr_title}`').format(**data))}
59
60 * ${h.literal(_('Commit flow: {source_ref_type}:{source_ref_name} of {source_repo_url} into {target_ref_type}:{target_ref_name} of {target_repo_url}').format(**data))}
46 61
47 62 * ${_('Comment link')}: ${pr_comment_url}
48 63
49 * ${_('Source repository')}: ${pr_source_repo_url}
64 %if status_change and not closing_pr:
65 * ${_('{user} submitted pull request !{pr_id} status: *{status}*').format(**data)}
50 66
67 %elif status_change and closing_pr:
68 * ${_('{user} submitted pull request !{pr_id} status: *{status} and closed*').format(**data)}
69
70 %endif
51 71 %if comment_file:
52 * ${_('File: {comment_file} on line {comment_line}').format(comment_file=comment_file, comment_line=comment_line)}
72 * ${_('File: {comment_file} on line {comment_line}').format(**data)}
73
53 74 %endif
75 % if comment_type == 'todo':
76 ${_('`TODO` comment')}:
77 % else:
78 ${_('`Note` comment')}:
79 % endif
80
81 ${comment_body |n, trim}
54 82
55 83 ---
56
57 %if status_change and not closing_pr:
58 ${_('{user} submitted pull request #{pr_id} status: *{status}*').format(**data)}
59 %elif status_change and closing_pr:
60 ${_('{user} submitted pull request #{pr_id} status: *{status} and closed*').format(**data)}
61 %endif
62
63 ${comment_body |n}
64
65 84 ${self.plaintext_footer()}
66 85 </%def>
67 86
@@ -69,46 +88,104 b' data = {'
69 88 <%
70 89 data = {
71 90 'user': h.person(user),
72 'pr_title': pull_request.title,
73 'pr_id': pull_request.pull_request_id,
74 'status': status_change,
75 91 'comment_file': comment_file,
76 92 'comment_line': comment_line,
77 93 'comment_type': comment_type,
94 'renderer_type': renderer_type or 'plain',
95
96 'pr_title': pull_request.title,
97 'pr_id': pull_request.pull_request_id,
98 'status': status_change,
99 'source_ref_type': pull_request.source_ref_parts.type,
100 'source_ref_name': pull_request.source_ref_parts.name,
101 'target_ref_type': pull_request.target_ref_parts.type,
102 'target_ref_name': pull_request.target_ref_parts.name,
103 'source_repo': pull_request_source_repo.repo_name,
104 'target_repo': pull_request_target_repo.repo_name,
105 'source_repo_url': h.link_to(pull_request_source_repo.repo_name, pull_request_source_repo_url),
106 'target_repo_url': h.link_to(pull_request_target_repo.repo_name, pull_request_target_repo_url),
78 107 }
79 108 %>
80 <table style="text-align:left;vertical-align:middle;">
81 <tr><td colspan="2" style="width:100%;padding-bottom:15px;border-bottom:1px solid #dbd9da;">
109
110 <table style="text-align:left;vertical-align:middle;width: 100%">
111 <tr>
112 <td style="width:100%;border-bottom:1px solid #dbd9da;">
82 113
83 % if comment_file:
84 <h4><a href="${pr_comment_url}" style="color:#427cc9;text-decoration:none;cursor:pointer">${_('{user} left a {comment_type} on file `{comment_file}` in pull request #{pr_id} "{pr_title}"').format(**data) |n}</a></h4>
85 % else:
86 <h4><a href="${pr_comment_url}" style="color:#427cc9;text-decoration:none;cursor:pointer">${_('{user} left a {comment_type} on pull request #{pr_id} "{pr_title}"').format(**data) |n}</a></h4>
87 % endif
114 <h4 style="margin: 0">
115 <div style="margin-bottom: 4px; color:#7E7F7F">
116 @${h.person(user.username)}
117 </div>
118 ${_('left a')}
119 <a href="${pr_comment_url}" style="${base.link_css()}">
120 % if comment_file:
121 ${_('{comment_type} on file `{comment_file}` in pull request.').format(**data)}
122 % else:
123 ${_('{comment_type} on pull request.').format(**data) |n}
124 % endif
125 </a>
126 <div style="margin-top: 10px"></div>
127 ${_('Pull request')} <code>!${data['pr_id']}: ${data['pr_title']}</code>
128 </h4>
88 129
89 </td></tr>
90 <tr><td style="padding-right:20px;padding-top:15px;">${_('Source')}</td><td style="padding-top:15px;"><a style="color:#427cc9;text-decoration:none;cursor:pointer" href="${pr_source_repo_url}">${pr_source_repo.repo_name}</a></td></tr>
130 </td>
131 </tr>
132
133 </table>
134
135 <table style="text-align:left;vertical-align:middle;width: 100%">
136
137 ## spacing def
138 <tr>
139 <td style="width: 130px"></td>
140 <td></td>
141 </tr>
91 142
92 143 % if status_change:
144 <tr>
145 <td style="padding-right:20px;">${_('Review Status')}:</td>
146 <td>
147 % if closing_pr:
148 ${_('Closed pull request with status')}: ${base.status_text(status_change, tag_type=status_change_type)}
149 % else:
150 ${_('Submitted review status')}: ${base.status_text(status_change, tag_type=status_change_type)}
151 % endif
152 </td>
153 </tr>
154 % endif
155
156 <tr>
157 <td style="padding-right:20px;line-height:20px;">${_('Commit Flow')}:</td>
158 <td style="line-height:20px;">
159 ${base.tag_button('{}:{}'.format(data['source_ref_type'], pull_request.source_ref_parts.name))} ${_('of')} ${data['source_repo_url']}
160 &rarr;
161 ${base.tag_button('{}:{}'.format(data['target_ref_type'], pull_request.target_ref_parts.name))} ${_('of')} ${data['target_repo_url']}
162 </td>
163 </tr>
164 <tr>
165 <td style="padding-right:20px;">${_('Pull request')}:</td>
166 <td>
167 <a href="${pull_request_url}" style="${base.link_css()}">
168 !${pull_request.pull_request_id}
169 </a>
170 </td>
171 </tr>
172 % if comment_file:
93 173 <tr>
94 <td style="padding-right:20px;">${_('Status')}</td>
95 <td>
96 % if closing_pr:
97 ${_('Closed pull request with status')}: ${base.status_text(status_change, tag_type=status_change_type)}
98 % else:
99 ${_('Submitted review status')}: ${base.status_text(status_change, tag_type=status_change_type)}
100 % endif
101 </td>
174 <td style="padding-right:20px;">${_('File')}:</td>
175 <td><a href="${pr_comment_url}" style="${base.link_css()}">${_('`{comment_file}` on line {comment_line}').format(**data)}</a></td>
102 176 </tr>
103 177 % endif
104 <tr>
105 <td style="padding-right:20px;">
178
179 <tr style="background-image: linear-gradient(to right, black 33%, rgba(255,255,255,0) 0%);background-position: bottom;background-size: 3px 1px;background-repeat: repeat-x;">
180 <td colspan="2" style="padding-right:20px;">
106 181 % if comment_type == 'todo':
107 ${(_('TODO comment on line: {comment_line}') if comment_file else _('TODO comment')).format(**data)}
182 ${_('`TODO` comment')}:
108 183 % else:
109 ${(_('Note comment on line: {comment_line}') if comment_file else _('Note comment')).format(**data)}
184 ${_('`Note` comment')}:
110 185 % endif
111 186 </td>
112 <td style="line-height:1.2em;white-space:pre-wrap">${h.render(comment_body, renderer=renderer_type, mentions=True)}</td>
187 </tr>
188
189 <td colspan="2" style="background: #F7F7F7">${h.render(comment_body, renderer=data['renderer_type'], mentions=True)}</td>
113 190 </tr>
114 191 </table>
@@ -2,19 +2,20 b''
2 2 <%inherit file="base.mako"/>
3 3 <%namespace name="base" file="base.mako"/>
4 4
5 ## EMAIL SUBJECT
5 6 <%def name="subject()" filter="n,trim,whitespace_filter">
6 7 <%
7 8 data = {
8 'user': h.person(user),
9 'user': '@'+h.person(user),
9 10 'pr_id': pull_request.pull_request_id,
10 11 'pr_title': pull_request.title,
11 12 }
12 13 %>
13 14
14 ${_('%(user)s wants you to review pull request #%(pr_id)s: "%(pr_title)s"') % data |n}
15 ${_('{user} requested a pull request review. !{pr_id}: "{pr_title}"').format(**data) |n}
15 16 </%def>
16 17
17
18 ## PLAINTEXT VERSION OF BODY
18 19 <%def name="body_plaintext()" filter="n,trim">
19 20 <%
20 21 data = {
@@ -25,32 +26,36 b' data = {'
25 26 'source_ref_name': pull_request.source_ref_parts.name,
26 27 'target_ref_type': pull_request.target_ref_parts.type,
27 28 'target_ref_name': pull_request.target_ref_parts.name,
28 'repo_url': pull_request_source_repo_url
29 'repo_url': pull_request_source_repo_url,
30 'source_repo': pull_request_source_repo.repo_name,
31 'target_repo': pull_request_target_repo.repo_name,
32 'source_repo_url': pull_request_source_repo_url,
33 'target_repo_url': pull_request_target_repo_url,
29 34 }
30 35 %>
31 ${self.subject()}
32 36
37 ${h.literal(_('Pull request !{pr_id}: `{pr_title}`').format(**data))}
33 38
34 ${h.literal(_('Pull request from %(source_ref_type)s:%(source_ref_name)s of %(repo_url)s into %(target_ref_type)s:%(target_ref_name)s') % data)}
39 * ${h.literal(_('Commit flow: {source_ref_type}:{source_ref_name} of {source_repo_url} into {target_ref_type}:{target_ref_name} of {target_repo_url}').format(**data))}
35 40
36
37 * ${_('Link')}: ${pull_request_url}
41 * ${_('Pull Request link')}: ${pull_request_url}
38 42
39 43 * ${_('Title')}: ${pull_request.title}
40 44
41 45 * ${_('Description')}:
42 46
43 ${pull_request.description}
47 ${pull_request.description | trim}
44 48
45 49
46 50 * ${_ungettext('Commit (%(num)s)', 'Commits (%(num)s)', len(pull_request_commits) ) % {'num': len(pull_request_commits)}}:
47 51
48 52 % for commit_id, message in pull_request_commits:
49 - ${h.short_id(commit_id)}
50 ${h.chop_at_smart(message, '\n', suffix_if_chopped='...')}
53 - ${h.short_id(commit_id)}
54 ${h.chop_at_smart(message, '\n', suffix_if_chopped='...')}
51 55
52 56 % endfor
53 57
58 ---
54 59 ${self.plaintext_footer()}
55 60 </%def>
56 61 <%
@@ -63,23 +68,77 b' data = {'
63 68 'target_ref_type': pull_request.target_ref_parts.type,
64 69 'target_ref_name': pull_request.target_ref_parts.name,
65 70 'repo_url': pull_request_source_repo_url,
71 'source_repo': pull_request_source_repo.repo_name,
72 'target_repo': pull_request_target_repo.repo_name,
66 73 'source_repo_url': h.link_to(pull_request_source_repo.repo_name, pull_request_source_repo_url),
67 'target_repo_url': h.link_to(pull_request_target_repo.repo_name, pull_request_target_repo_url)
74 'target_repo_url': h.link_to(pull_request_target_repo.repo_name, pull_request_target_repo_url),
68 75 }
69 76 %>
70 <table style="text-align:left;vertical-align:middle;">
71 <tr><td colspan="2" style="width:100%;padding-bottom:15px;border-bottom:1px solid #dbd9da;"><h4><a href="${pull_request_url}" style="color:#427cc9;text-decoration:none;cursor:pointer">${_('%(user)s wants you to review pull request #%(pr_id)s: "%(pr_title)s".') % data }</a></h4></td></tr>
72 <tr><td style="padding-right:20px;padding-top:15px;">${_('Title')}</td><td style="padding-top:15px;">${pull_request.title}</td></tr>
73 <tr><td style="padding-right:20px;">${_('Source')}</td><td>${base.tag_button(pull_request.source_ref_parts.name)} ${h.literal(_('%(source_ref_type)s of %(source_repo_url)s') % data)}</td></tr>
74 <tr><td style="padding-right:20px;">${_('Target')}</td><td>${base.tag_button(pull_request.target_ref_parts.name)} ${h.literal(_('%(target_ref_type)s of %(target_repo_url)s') % data)}</td></tr>
75 <tr><td style="padding-right:20px;">${_('Description')}</td><td style="white-space:pre-wrap">${pull_request.description}</td></tr>
76 <tr><td style="padding-right:20px;">${_ungettext('%(num)s Commit', '%(num)s Commits', len(pull_request_commits)) % {'num': len(pull_request_commits)}}</td>
77 <td><ol style="margin:0 0 0 1em;padding:0;text-align:left;">
78 % for commit_id, message in pull_request_commits:
79 <li style="margin:0 0 1em;"><pre style="margin:0 0 .5em">${h.short_id(commit_id)}</pre>
80 ${h.chop_at_smart(message, '\n', suffix_if_chopped='...')}
81 </li>
82 % endfor
83 </ol></td>
77
78 <table style="text-align:left;vertical-align:middle;width: 100%">
79 <tr>
80 <td style="width:100%;border-bottom:1px solid #dbd9da;">
81
82 <h4 style="margin: 0">
83 <div style="margin-bottom: 4px; color:#7E7F7F">
84 @${h.person(user.username)}
85 </div>
86 ${_('requested a')}
87 <a href="${pull_request_url}" style="${base.link_css()}">
88 ${_('pull request review.').format(**data) }
89 </a>
90 <div style="margin-top: 10px"></div>
91 ${_('Pull request')} <code>!${data['pr_id']}: ${data['pr_title']}</code>
92 </h4>
93
94 </td>
95 </tr>
96
97 </table>
98
99 <table style="text-align:left;vertical-align:middle;width: 100%">
100 ## spacing def
101 <tr>
102 <td style="width: 130px"></td>
103 <td></td>
104 </tr>
105
106 <tr>
107 <td style="padding-right:20px;line-height:20px;">${_('Commit Flow')}:</td>
108 <td style="line-height:20px;">
109 ${base.tag_button('{}:{}'.format(data['source_ref_type'], pull_request.source_ref_parts.name))} ${_('of')} ${data['source_repo_url']}
110 &rarr;
111 ${base.tag_button('{}:{}'.format(data['target_ref_type'], pull_request.target_ref_parts.name))} ${_('of')} ${data['target_repo_url']}
112 </td>
113 </tr>
114
115 <tr>
116 <td style="padding-right:20px;">${_('Pull request')}:</td>
117 <td>
118 <a href="${pull_request_url}" style="${base.link_css()}">
119 !${pull_request.pull_request_id}
120 </a>
121 </td>
122 </tr>
123 <tr>
124 <td style="padding-right:20px;">${_('Description')}:</td>
125 <td style="white-space:pre-wrap"><code>${pull_request.description | trim}</code></td>
126 </tr>
127 <tr>
128 <td style="padding-right:20px;">${_ungettext('Commit (%(num)s)', 'Commits (%(num)s)', len(pull_request_commits)) % {'num': len(pull_request_commits)}}:</td>
129 <td></td>
130 </tr>
131
132 <tr>
133 <td colspan="2">
134 <ol style="margin:0 0 0 1em;padding:0;text-align:left;">
135 % for commit_id, message in pull_request_commits:
136 <li style="margin:0 0 1em;">
137 <pre style="margin:0 0 .5em"><a href="${h.route_path('repo_commit', repo_name=pull_request_source_repo.repo_name, commit_id=commit_id)}" style="${base.link_css()}">${h.short_id(commit_id)}</a></pre>
138 ${h.chop_at_smart(message, '\n', suffix_if_chopped='...')}
139 </li>
140 % endfor
141 </ol>
142 </td>
84 143 </tr>
85 144 </table>
@@ -15,7 +15,8 b' Email Plaintext Body'
15 15 </%def>
16 16
17 17 ## BODY GOES BELOW
18 <b>Email Body</b>
19
20 ${h.short_id('0' * 40)}
21 ${_('Translation')} No newline at end of file
18 <strong>Email Body</strong>
19 <br/>
20 <br/>
21 `h.short_id()`: ${h.short_id('0' * 40)}<br/>
22 ${_('Translation String')}<br/>
@@ -1,5 +1,6 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base.mako"/>
3 <%namespace name="base" file="base.mako"/>
3 4
4 5 <%def name="subject()" filter="n,trim,whitespace_filter">
5 6 RhodeCode new user registration: ${user.username}
@@ -14,14 +15,45 b' A new user `${user.username}` has regist'
14 15 - Email: ${user.email}
15 16 - Profile link: ${h.route_url('user_profile', username=user.username)}
16 17
18 ---
17 19 ${self.plaintext_footer()}
18 20 </%def>
19 21
20 ## BODY GOES BELOW
21 <table style="text-align:left;vertical-align:middle;">
22 <tr><td colspan="2" style="width:100%;padding-bottom:15px;border-bottom:1px solid #dbd9da;"><h4><a href="${h.route_url('user_profile', username=user.username)}" style="color:#427cc9;text-decoration:none;cursor:pointer">${_('New user %(user)s has registered on %(date)s') % {'user': user.username, 'date': h.format_date(date)}}</a></h4></td></tr>
23 <tr><td style="padding-right:20px;padding-top:20px;">${_('Username')}</td><td style="line-height:1;padding-top:20px;"><img style="margin-bottom:-5px;text-align:left;border:1px solid #dbd9da" src="${h.gravatar_url(user.email, 16)}" height="16" width="16">&nbsp;${user.username}</td></tr>
24 <tr><td style="padding-right:20px;">${_('Full Name')}</td><td>${user.first_name} ${user.last_name}</td></tr>
25 <tr><td style="padding-right:20px;">${_('Email')}</td><td>${user.email}</td></tr>
26 <tr><td style="padding-right:20px;">${_('Profile')}</td><td><a href="${h.route_url('user_profile', username=user.username)}">${h.route_url('user_profile', username=user.username)}</a></td></tr>
27 </table> No newline at end of file
22
23 <table style="text-align:left;vertical-align:middle;width: 100%">
24 <tr>
25 <td style="width:100%;border-bottom:1px solid #dbd9da;">
26 <h4 style="margin: 0">
27 <a href="${h.route_url('user_profile', username=user.username)}" style="${base.link_css()}">
28 ${_('New user {user} has registered on {date}').format(user=user.username, date=h.format_date(date))}
29 </a>
30 </h4>
31 </td>
32 </tr>
33 </table>
34
35 <table style="text-align:left;vertical-align:middle;width: 100%">
36 ## spacing def
37 <tr>
38 <td style="width: 130px"></td>
39 <td></td>
40 </tr>
41 <tr>
42 <td style="padding-right:20px;padding-top:20px;">${_('Username')}:</td>
43 <td style="line-height:1;padding-top:20px;">${user.username}</td>
44 </tr>
45 <tr>
46 <td style="padding-right:20px;">${_('Full Name')}:</td>
47 <td>${user.first_name} ${user.last_name}</td>
48 </tr>
49 <tr>
50 <td style="padding-right:20px;">${_('Email')}:</td>
51 <td>${user.email}</td>
52 </tr>
53 <tr>
54 <td style="padding-right:20px;">${_('Profile')}:</td>
55 <td>
56 <a href="${h.route_url('user_profile', username=user.username)}">${h.route_url('user_profile', username=user.username)}</a>
57 </td>
58 </tr>
59 </table>
@@ -1,4 +1,3 b''
1 import collections
2 1 # -*- coding: utf-8 -*-
3 2
4 3 # Copyright (C) 2010-2019 RhodeCode GmbH
@@ -20,9 +19,11 b' import collections'
20 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
21 20
22 21 import pytest
22 import collections
23 23
24 24 from rhodecode.lib.partial_renderer import PyramidPartialRenderer
25 25 from rhodecode.lib.utils2 import AttributeDict
26 from rhodecode.model.db import User
26 27 from rhodecode.model.notification import EmailNotificationModel
27 28
28 29
@@ -47,29 +48,26 b' def test_render_email(app, http_host_onl'
47 48 assert body_plaintext == 'Email Plaintext Body'
48 49
49 50 # body
50 notification_footer = 'This is a notification from RhodeCode. http://%s/' \
51 % http_host_only_stub
52 assert notification_footer in body
51 notification_footer1 = 'This is a notification from RhodeCode.'
52 notification_footer2 = 'http://{}/'.format(http_host_only_stub)
53 assert notification_footer1 in body
54 assert notification_footer2 in body
53 55 assert 'Email Body' in body
54 56
55 57
56 58 def test_render_pr_email(app, user_admin):
57
58 ref = collections.namedtuple('Ref',
59 'name, type')(
60 'fxies123', 'book'
61 )
59 ref = collections.namedtuple(
60 'Ref', 'name, type')('fxies123', 'book')
62 61
63 62 pr = collections.namedtuple('PullRequest',
64 63 'pull_request_id, title, description, source_ref_parts, source_ref_name, target_ref_parts, target_ref_name')(
65 64 200, 'Example Pull Request', 'Desc of PR', ref, 'bookmark', ref, 'Branch')
66 65
67 source_repo = target_repo = collections.namedtuple('Repo',
68 'type, repo_name')(
69 'hg', 'pull_request_1')
66 source_repo = target_repo = collections.namedtuple(
67 'Repo', 'type, repo_name')('hg', 'pull_request_1')
70 68
71 69 kwargs = {
72 'user': '<marcin@rhodecode.com> Marcin Kuzminski',
70 'user': User.get_first_super_admin(),
73 71 'pull_request': pr,
74 72 'pull_request_commits': [],
75 73
@@ -86,7 +84,7 b' def test_render_pr_email(app, user_admin'
86 84 EmailNotificationModel.TYPE_PULL_REQUEST, **kwargs)
87 85
88 86 # subject
89 assert subject == 'Marcin Kuzminski wants you to review pull request #200: "Example Pull Request"'
87 assert subject == '@test_admin (RhodeCode Admin) requested a pull request review. !200: "Example Pull Request"'
90 88
91 89
92 90 @pytest.mark.parametrize('mention', [
@@ -98,24 +96,21 b' def test_render_pr_email(app, user_admin'
98 96 EmailNotificationModel.TYPE_PULL_REQUEST_COMMENT
99 97 ])
100 98 def test_render_comment_subject_no_newlines(app, mention, email_type):
101 ref = collections.namedtuple('Ref',
102 'name, type')(
103 'fxies123', 'book'
104 )
99 ref = collections.namedtuple(
100 'Ref', 'name, type')('fxies123', 'book')
105 101
106 102 pr = collections.namedtuple('PullRequest',
107 103 'pull_request_id, title, description, source_ref_parts, source_ref_name, target_ref_parts, target_ref_name')(
108 104 200, 'Example Pull Request', 'Desc of PR', ref, 'bookmark', ref, 'Branch')
109 105
110 source_repo = target_repo = collections.namedtuple('Repo',
111 'type, repo_name')(
112 'hg', 'pull_request_1')
106 source_repo = target_repo = collections.namedtuple(
107 'Repo', 'type, repo_name')('hg', 'pull_request_1')
113 108
114 109 kwargs = {
115 'user': '<marcin@rhodecode.com> Marcin Kuzminski',
110 'user': User.get_first_super_admin(),
116 111 'commit': AttributeDict(raw_id='a'*40, message='Commit message'),
117 112 'status_change': 'approved',
118 'commit_target_repo': AttributeDict(),
113 'commit_target_repo_url': 'http://foo.example.com/#comment1',
119 114 'repo_name': 'test-repo',
120 115 'comment_file': 'test-file.py',
121 116 'comment_line': 'n100',
@@ -126,8 +121,6 b' def test_render_comment_subject_no_newli'
126 121 'mention': mention,
127 122
128 123 'pr_comment_url': 'http://comment-url',
129 'pr_source_repo': AttributeDict(repo_name='foobar'),
130 'pr_source_repo_url': 'http://soirce-repo/url',
131 124 'pull_request': pr,
132 125 'pull_request_commits': [],
133 126
@@ -136,6 +129,8 b' def test_render_comment_subject_no_newli'
136 129
137 130 'pull_request_source_repo': source_repo,
138 131 'pull_request_source_repo_url': 'x',
132
133 'pull_request_url': 'http://code.rc.com/_pr/123'
139 134 }
140 135 subject, headers, body, body_plaintext = EmailNotificationModel().render_email(
141 136 email_type, **kwargs)
General Comments 0
You need to be logged in to leave comments. Login now