Browse Source

wwebsite_url required + fix most url issue for mail_template

Guénaël Muller 5 years ago
parent
commit
420e75fd57

+ 9 - 3
backend/tests_configs.ini View File

@@ -4,7 +4,7 @@ depot_storage_name = test
4 4
 depot_storage_dir = /tmp/test/depot
5 5
 user.auth_token.validity = 604800
6 6
 preview_cache_dir = /tmp/test/preview_cache
7
-
7
+website.base_url = http://localhost:6543
8 8
 [app:command_test]
9 9
 use = egg:tracim_backend
10 10
 sqlalchemy.url = sqlite:///tracim_test.sqlite
@@ -12,6 +12,7 @@ depot_storage_name = test
12 12
 depot_storage_dir = /tmp/test/depot
13 13
 user.auth_token.validity = 604800
14 14
 preview_cache_dir = /tmp/test/preview_cache
15
+website.base_url = http://localhost:6543
15 16
 
16 17
 [mail_test]
17 18
 sqlalchemy.url = sqlite:///:memory:
@@ -37,6 +38,7 @@ email.notification.smtp.server = 127.0.0.1
37 38
 email.notification.smtp.port = 1025
38 39
 email.notification.smtp.user = test_user
39 40
 email.notification.smtp.password = just_a_password
41
+website.base_url = http://localhost:6543
40 42
 
41 43
 [mail_test_async]
42 44
 sqlalchemy.url = sqlite:///:memory:
@@ -63,6 +65,7 @@ email.notification.smtp.server = 127.0.0.1
63 65
 email.notification.smtp.port = 1025
64 66
 email.notification.smtp.user = test_user
65 67
 email.notification.smtp.password = just_a_password
68
+website.base_url = http://localhost:6543
66 69
 
67 70
 [functional_test]
68 71
 sqlalchemy.url = sqlite:///tracim_test.sqlite
@@ -72,6 +75,7 @@ user.auth_token.validity = 604800
72 75
 preview_cache_dir = /tmp/test/preview_cache
73 76
 preview.jpg.restricted_dims = True
74 77
 email.notification.activated = false
78
+website.base_url = http://localhost:6543
75 79
 
76 80
 [functional_test_no_db]
77 81
 sqlalchemy.url = sqlite://
@@ -81,6 +85,7 @@ user.auth_token.validity = 604800
81 85
 preview_cache_dir = /tmp/test/preview_cache
82 86
 preview.jpg.restricted_dims = True
83 87
 email.notification.activated = false
88
+website.base_url = http://localhost:6543
84 89
 
85 90
 [functional_test_with_mail_test_sync]
86 91
 sqlalchemy.url = sqlite:///tracim_test.sqlite
@@ -105,7 +110,7 @@ email.notification.smtp.server = 127.0.0.1
105 110
 email.notification.smtp.port = 1025
106 111
 email.notification.smtp.user = test_user
107 112
 email.notification.smtp.password = just_a_password
108
-
113
+website.base_url = http://localhost:6543
109 114
 
110 115
 [functional_test_with_mail_test_async]
111 116
 sqlalchemy.url = sqlite:///tracim_test.sqlite
@@ -129,4 +134,5 @@ email.notification.processing_mode = async
129 134
 email.notification.smtp.server = 127.0.0.1
130 135
 email.notification.smtp.port = 1025
131 136
 email.notification.smtp.user = test_user
132
-email.notification.smtp.password = just_a_password
137
+email.notification.smtp.password = just_a_password
138
+website.base_url = http://localhost:6543

+ 6 - 0
backend/tracim_backend/config.py View File

@@ -81,6 +81,12 @@ class CFG(object):
81 81
             'website.base_url',
82 82
             '',
83 83
         )
84
+        if not self.WEBSITE_BASE_URL:
85
+            raise Exception(
86
+                'website.base_url is needed in order to have correct path in'
87
+                'few place like in email.'
88
+                'You should set it with frontend root url.'
89
+            )
84 90
 
85 91
         # TODO - G.M - 26-03-2018 - [Cleanup] These params seems deprecated for tracimv2,  # nopep8
86 92
         # Verify this

+ 36 - 16
backend/tracim_backend/lib/mail_notifier/notifier.py View File

@@ -17,9 +17,12 @@ from tracim_backend.lib.mail_notifier.utils import SmtpConfiguration, EST
17 17
 from tracim_backend.lib.mail_notifier.sender import send_email_through
18 18
 from tracim_backend.lib.core.workspace import WorkspaceApi
19 19
 from tracim_backend.lib.utils.logger import logger
20
-from tracim_backend.models import User
20
+from tracim_backend.lib.utils.utils import get_login_frontend_url
21
+from tracim_backend.lib.utils.utils import get_email_logo_frontend_url
21 22
 from tracim_backend.models.auth import User
22 23
 from tracim_backend.models.contents import CONTENT_TYPES
24
+from tracim_backend.models.context_models import ContentInContext
25
+from tracim_backend.models.context_models import WorkspaceInContext
23 26
 from tracim_backend.models.data import ActionDescription
24 27
 from tracim_backend.models.data import Content
25 28
 from tracim_backend.models.data import UserRoleInWorkspace
@@ -234,7 +237,13 @@ class EmailManager(object):
234 237
             show_archived=True,
235 238
             show_deleted=True,
236 239
         ).get_one(event_content_id, CONTENT_TYPES.Any_SLUG)
237
-        main_content = content.parent if content.type == CONTENT_TYPES.Comment.slug else content
240
+        workspace_api = WorkspaceApi(
241
+            session=self.session,
242
+            current_user=user,
243
+            config=self.config,
244
+        )
245
+        workpace_in_context = workspace_api.get_workspace_with_context(workspace_api.get_one(content.workspace_id))  # nopep8
246
+        main_content = content.parent if content.type == CONTENT_TYPES.Comment.slug else content  # nopep8
238 247
         notifiable_roles = WorkspaceApi(
239 248
             current_user=user,
240 249
             session=self.session,
@@ -265,7 +274,7 @@ class EmailManager(object):
265 274
             # INFO - G.M - 2017-11-15 - set content_id in header to permit reply
266 275
             # references can have multiple values, but only one in this case.
267 276
             replyto_addr = self.config.EMAIL_NOTIFICATION_REPLY_TO_EMAIL.replace( # nopep8
268
-                '{content_id}',str(content.content_id)
277
+                '{content_id}', str(content.content_id)
269 278
             )
270 279
 
271 280
             reference_addr = self.config.EMAIL_NOTIFICATION_REFERENCES_EMAIL.replace( #nopep8
@@ -297,8 +306,21 @@ class EmailManager(object):
297 306
             # To link this email to a content we create a virtual parent
298 307
             # in reference who contain the content_id.
299 308
             message['References'] = formataddr(('',reference_addr))
300
-            body_text = self._build_email_body_for_content(self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_TEXT, role, content, user)
301
-            body_html = self._build_email_body_for_content(self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_HTML, role, content, user)
309
+            content_in_context = content_api.get_content_in_context(content)
310
+            body_text = self._build_email_body_for_content(
311
+                self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_TEXT,
312
+                role,
313
+                content_in_context,
314
+                workpace_in_context,
315
+                user
316
+            )
317
+            body_html = self._build_email_body_for_content(
318
+                self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_HTML,
319
+                role,
320
+                content_in_context,
321
+                workpace_in_context,
322
+                user
323
+            )
302 324
 
303 325
             part1 = MIMEText(body_text, 'plain', 'utf-8')
304 326
             part2 = MIMEText(body_html, 'html', 'utf-8')
@@ -362,9 +384,9 @@ class EmailManager(object):
362 384
             'user': user,
363 385
             'password': password,
364 386
             # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for logo_url  # nopep8
365
-            'logo_url': '',
387
+            'logo_url': get_email_logo_frontend_url(self.config),
366 388
             # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for login_url  # nopep8
367
-            'login_url': self.config.WEBSITE_BASE_URL,
389
+            'login_url': get_login_frontend_url(self.config),
368 390
         }
369 391
         body_text = self._render_template(
370 392
             mako_template_filepath=text_template_file_path,
@@ -415,8 +437,9 @@ class EmailManager(object):
415 437
             self,
416 438
             mako_template_filepath: str,
417 439
             role: UserRoleInWorkspace,
418
-            content: Content,
419
-            actor: User
440
+            content_in_context: ContentInContext,
441
+            workspace_in_context: WorkspaceInContext,
442
+            actor: User,
420 443
     ) -> str:
421 444
         """
422 445
         Build an email body and return it as a string
@@ -424,24 +447,21 @@ class EmailManager(object):
424 447
         :param role: the role related to user to whom the email must be sent. The role is required (and not the user only) in order to show in the mail why the user receive the notification
425 448
         :param content: the content item related to the notification
426 449
         :param actor: the user at the origin of the action / notification (for example the one who wrote a comment
427
-        :param config: the global configuration
428 450
         :return: the built email body as string. In case of multipart email, this method must be called one time for text and one time for html
429 451
         """
430 452
         logger.debug(self, 'Building email content from MAKO template {}'.format(mako_template_filepath))
431
-
453
+        content = content_in_context.content
432 454
         main_title = content.label
433 455
         content_intro = ''
434 456
         content_text = ''
435 457
         call_to_action_text = ''
436 458
 
437
-        # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for call_to_action_url  # nopep8
438
-        call_to_action_url =''
459
+        call_to_action_url = content_in_context.frontend_url
439 460
         # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for status_icon_url  # nopep8
440 461
         status_icon_url = ''
441
-        # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for workspace_url  # nopep8
442
-        workspace_url = ''
462
+        workspace_url = workspace_in_context.frontend_url
443 463
         # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for logo_url  # nopep8
444
-        logo_url = ''
464
+        logo_url = get_email_logo_frontend_url(self.config)
445 465
 
446 466
         action = content.get_last_action().id
447 467
         if ActionDescription.COMMENT == action:

+ 29 - 0
backend/tracim_backend/lib/utils/utils.py View File

@@ -2,6 +2,8 @@
2 2
 import datetime
3 3
 import random
4 4
 import string
5
+from enum import Enum
6
+
5 7
 from redis import Redis
6 8
 from rq import Queue
7 9
 
@@ -10,6 +12,32 @@ from tracim_backend.config import CFG
10 12
 DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
11 13
 DEFAULT_WEBDAV_CONFIG_FILE = "wsgidav.conf"
12 14
 DEFAULT_TRACIM_CONFIG_FILE = "development.ini"
15
+CONTENT_FRONTEND_URL_SCHEMA = 'workspaces/{workspace_id}/contents/{content_type}/{content_id}'  # nopep8
16
+WORKSPACE_FRONTEND_URL_SCHEMA = 'workspaces/{workspace_id}'  # nopep8
17
+
18
+
19
+def get_root_frontend_url(config: CFG) -> str:
20
+    """
21
+    Return website base url with always '/' at the end
22
+    """
23
+    base_url = ''
24
+    if config.WEBSITE_BASE_URL[-1] == '/':
25
+        base_url = config.WEBSITE_BASE_URL
26
+    else:
27
+        base_url = config.WEBSITE_BASE_URL + '/'
28
+    return base_url
29
+
30
+
31
+def get_login_frontend_url(config: CFG):
32
+    """
33
+    Return login page url
34
+    """
35
+    return get_root_frontend_url(config) + 'login'
36
+
37
+
38
+def get_email_logo_frontend_url(config: CFG):
39
+    # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for email_logo_frontend_url  # nopep8
40
+    return 'data:image/png;base64,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'  # nopep8'
13 41
 
14 42
 
15 43
 def get_redis_connection(config: CFG) -> Redis:
@@ -96,3 +124,4 @@ def password_generator(
96 124
     :return: password as string
97 125
     """
98 126
     return ''.join(random.choice(chars) for char_number in range(length))
127
+

+ 22 - 1
backend/tracim_backend/models/context_models.py View File

@@ -7,6 +7,9 @@ from slugify import slugify
7 7
 from sqlalchemy.orm import Session
8 8
 from tracim_backend.config import CFG
9 9
 from tracim_backend.config import PreviewDim
10
+from tracim_backend.lib.utils.utils import get_root_frontend_url
11
+from tracim_backend.lib.utils.utils import CONTENT_FRONTEND_URL_SCHEMA
12
+from tracim_backend.lib.utils.utils import WORKSPACE_FRONTEND_URL_SCHEMA
10 13
 from tracim_backend.models import User
11 14
 from tracim_backend.models.auth import Profile
12 15
 from tracim_backend.models.data import Content
@@ -14,7 +17,7 @@ from tracim_backend.models.data import ContentRevisionRO
14 17
 from tracim_backend.models.data import Workspace
15 18
 from tracim_backend.models.data import UserRoleInWorkspace
16 19
 from tracim_backend.models.roles import WorkspaceRoles
17
-from tracim_backend.models.workspace_menu_entries import default_workspace_menu_entry
20
+from tracim_backend.models.workspace_menu_entries import default_workspace_menu_entry  # nopep8
18 21
 from tracim_backend.models.workspace_menu_entries import WorkspaceMenuEntry
19 22
 from tracim_backend.models.contents import CONTENT_TYPES
20 23
 
@@ -462,6 +465,14 @@ class WorkspaceInContext(object):
462 465
         # apps)
463 466
         return default_workspace_menu_entry(self.workspace)
464 467
 
468
+    @property
469
+    def frontend_url(self):
470
+        root_frontend_url = get_root_frontend_url(self.config)
471
+        workspace_frontend_url = WORKSPACE_FRONTEND_URL_SCHEMA.format(
472
+            workspace_id=self.workspace_id,
473
+        )
474
+        return root_frontend_url + workspace_frontend_url
475
+
465 476
 
466 477
 class UserRoleWorkspaceInContext(object):
467 478
     """
@@ -660,6 +671,16 @@ class ContentInContext(object):
660 671
         assert self._user
661 672
         return not self.content.has_new_information_for(self._user)
662 673
 
674
+    @property
675
+    def frontend_url(self):
676
+        root_frontend_url = get_root_frontend_url(self.config)
677
+        content_frontend_url = CONTENT_FRONTEND_URL_SCHEMA.format(
678
+            workspace_id=self.workspace_id,
679
+            content_type=self.content_type,
680
+            content_id=self.content_id,
681
+        )
682
+        return root_frontend_url + content_frontend_url
683
+
663 684
 
664 685
 class RevisionInContext(object):
665 686
     """

+ 3 - 1
backend/tracim_backend/templates/mail/content_update_body_html.mak View File

@@ -45,7 +45,7 @@
45 45
             ${main_title}
46 46
             &mdash;&nbsp;<span style="font-weight: bold; color: #999; font-weight: bold;">
47 47
               ${status_label|n}
48
-              <img alt="status_icon" src="${status_icon_url}" style="vertical-align: middle;">
48
+              <img alt="" src="${status_icon_url}" style="vertical-align: middle;">
49 49
             </span>
50 50
         </td>
51 51
       </tr>
@@ -55,6 +55,8 @@
55 55
     <div id="content-body">
56 56
         <div>${content_text|n}</div>
57 57
         <div href='' id="call-to-action-container">
58
+            <span style=""> <a href="${call_to_action_url}"
59
+            id="call-to-action-button">${call_to_action_text}</a> </span> </div>
58 60
         </div>
59 61
     </div>
60 62
     

+ 1 - 2
backend/tracim_backend/templates/mail/created_account_body_html.mak View File

@@ -68,10 +68,9 @@
68 68
         </div>
69 69
         <div id="call-to-action-container">
70 70
 
71
-            ${_('To go to {website_title}, please click on following link'.format(
71
+            ${_('To go to {website_title}, please click on following link :'.format(
72 72
                 website_title=config.WEBSITE_TITLE
73 73
             ))}
74
-
75 74
             <span style="">
76 75
                 <a href="${login_url}" id='call-to-action-button'>${login_url}</a>
77 76
             </span>

+ 1 - 0
backend/tracim_backend/tests/library/test_webdav.py View File

@@ -29,6 +29,7 @@ class TestWebdavFactory(StandardTest):
29 29
         :return:
30 30
         """
31 31
         tracim_settings = {
32
+            'website.base_url': 'http://localhost:6543',
32 33
             'sqlalchemy.url': 'sqlite:///:memory:',
33 34
             'user.auth_token.validity': '604800',
34 35
             'depot_storage_dir': '/tmp/test/depot',

+ 1 - 1
backend/tracim_backend/views/core_api/schemas.py View File

@@ -48,7 +48,7 @@ class UserDigestSchema(marshmallow.Schema):
48 48
     user_id = marshmallow.fields.Int(dump_only=True, example=3)
49 49
     avatar_url = marshmallow.fields.Url(
50 50
         allow_none=True,
51
-        example="/api/v2/assets/avatars/suri-cate.jpg",
51
+        example="/api/v2/asset/avatars/suri-cate.jpg",
52 52
         description="avatar_url is the url to the image file. "
53 53
                     "If no avatar, then set it to null "
54 54
                     "(and frontend will interpret this with a default avatar)",

+ 1 - 1
backend/tracim_backend/views/frontend.py View File

@@ -27,7 +27,7 @@ class FrontendController(Controller):
27 27
     def not_found_view(self, context, request: TracimRequest):
28 28
 
29 29
         if request.path.startswith(BASE_API_V2):
30
-            raise PageNotFound('{} is not a valid path'.format(request.path)) from context # nopep8
30
+            raise PageNotFound('{} is not a valid path'.format(request.path)) from context  # nopep8
31 31
         return self.index(context, request)
32 32
 
33 33
     def index(self, context, request: TracimRequest):