Bläddra i källkod

Merge pull request #25 from tracim/feature/734_add_lang_to_user

Bastien Sevajol 6 år sedan
förälder
incheckning
54f62ebe19
No account linked to committer's email

+ 6 - 0
backend/tracim_backend/lib/core/user.py Visa fil

@@ -314,6 +314,7 @@ class UserApi(object):
314 314
             email: str=None,
315 315
             password: str=None,
316 316
             timezone: str=None,
317
+            lang: str=None,
317 318
             groups: typing.Optional[typing.List[Group]]=None,
318 319
             do_save=True,
319 320
     ) -> User:
@@ -330,6 +331,9 @@ class UserApi(object):
330 331
         if timezone is not None:
331 332
             user.timezone = timezone
332 333
 
334
+        if lang is not None:
335
+            user.lang = lang
336
+
333 337
         if groups is not None:
334 338
             # INFO - G.M - 2018-07-18 - Delete old groups
335 339
             for group in user.groups:
@@ -351,6 +355,7 @@ class UserApi(object):
351 355
         password: str = None,
352 356
         name: str = None,
353 357
         timezone: str = '',
358
+        lang: str= None,
354 359
         groups=[],
355 360
         do_save: bool=True,
356 361
         do_notify: bool=True,
@@ -362,6 +367,7 @@ class UserApi(object):
362 367
             email=email,
363 368
             password=password,
364 369
             timezone=timezone,
370
+            lang=lang,
365 371
             do_save=False,
366 372
         )
367 373
         if do_notify:

+ 26 - 0
backend/tracim_backend/migration/versions/a143b60aad61_add_lang_to_user.py Visa fil

@@ -0,0 +1,26 @@
1
+"""add lang to user
2
+
3
+Revision ID: a143b60aad61
4
+Revises: 78b52ca39419
5
+Create Date: 2018-08-20 10:17:14.859250
6
+
7
+"""
8
+
9
+# revision identifiers, used by Alembic.
10
+revision = 'a143b60aad61'
11
+down_revision = '78b52ca39419'
12
+
13
+from alembic import op
14
+import sqlalchemy as sa
15
+
16
+
17
+def upgrade():
18
+    # ### commands auto generated by Alembic - please adjust! ###
19
+    op.add_column('users', sa.Column('lang', sa.Unicode(length=3), nullable=True))  # nopep8
20
+    # ### end Alembic commands ###
21
+
22
+
23
+def downgrade():
24
+    # ### commands auto generated by Alembic - please adjust! ###
25
+    op.drop_column('users', 'lang')
26
+    # ### end Alembic commands ###

+ 3 - 0
backend/tracim_backend/models/auth.py Visa fil

@@ -138,7 +138,10 @@ class User(DeclarativeBase):
138 138
     is_active = Column(Boolean, default=True, nullable=False)
139 139
     is_deleted = Column(Boolean, default=False, nullable=False, server_default=sqlalchemy.sql.expression.literal(False))
140 140
     imported_from = Column(Unicode(32), nullable=True)
141
+    # timezone as tz database format
141 142
     timezone = Column(Unicode(255), nullable=False, server_default='')
143
+    # lang in iso639 format
144
+    lang = Column(Unicode(3), nullable=True, default=None)
142 145
     # TODO - G.M - 04-04-2018 - [auth] Check if this is already needed
143 146
     # with new auth system
144 147
     auth_token = Column(Unicode(255))

+ 8 - 1
backend/tracim_backend/models/context_models.py Visa fil

@@ -82,9 +82,10 @@ class UserInfos(object):
82 82
     """
83 83
     Just some user infos
84 84
     """
85
-    def __init__(self, timezone: str, public_name: str) -> None:
85
+    def __init__(self, timezone: str, public_name: str, lang: str) -> None:
86 86
         self.timezone = timezone
87 87
         self.public_name = public_name
88
+        self.lang = lang
88 89
 
89 90
 
90 91
 class UserProfile(object):
@@ -106,6 +107,7 @@ class UserCreation(object):
106 107
             public_name: str = None,
107 108
             timezone: str = None,
108 109
             profile: str = None,
110
+            lang: str = None,
109 111
             email_notification: bool = True,
110 112
     ) -> None:
111 113
         self.email = email
@@ -114,6 +116,7 @@ class UserCreation(object):
114 116
         self.password = password or password_generator()
115 117
         self.public_name = public_name or None
116 118
         self.timezone = timezone or ''
119
+        self.lang = lang or None
117 120
         self.profile = profile or Group.TIM_USER_GROUPNAME
118 121
         self.email_notification = email_notification
119 122
 
@@ -412,6 +415,10 @@ class UserInContext(object):
412 415
         return self.user.timezone
413 416
 
414 417
     @property
418
+    def lang(self) -> str:
419
+        return self.user.lang
420
+
421
+    @property
415 422
     def profile(self) -> Profile:
416 423
         return self.user.profile.name
417 424
 

+ 2 - 0
backend/tracim_backend/tests/functional/test_session.py Visa fil

@@ -160,6 +160,7 @@ class TestWhoamiEndpoint(FunctionalTest):
160 160
         assert res.json_body['profile'] == 'administrators'
161 161
         assert res.json_body['caldav_url'] is None
162 162
         assert res.json_body['avatar_url'] is None
163
+        assert res.json_body['lang'] is None
163 164
 
164 165
     def test_api__try_whoami_enpoint__err_401__user_is_not_active(self):
165 166
         dbsession = get_tm_session(self.session_factory, transaction.manager)
@@ -183,6 +184,7 @@ class TestWhoamiEndpoint(FunctionalTest):
183 184
             name='bob',
184 185
             groups=groups,
185 186
             timezone='Europe/Paris',
187
+            lang='en',
186 188
             do_save=True,
187 189
             do_notify=False,
188 190
         )

+ 81 - 1
backend/tracim_backend/tests/functional/test_user.py Visa fil

@@ -76,6 +76,7 @@ class TestUserRecentlyActiveContentEndpoint(FunctionalTest):
76 76
             name='bob',
77 77
             groups=groups,
78 78
             timezone='Europe/Paris',
79
+            lang='fr',
79 80
             do_save=True,
80 81
             do_notify=False,
81 82
         )
@@ -200,6 +201,7 @@ class TestUserRecentlyActiveContentEndpoint(FunctionalTest):
200 201
             name='bob',
201 202
             groups=groups,
202 203
             timezone='Europe/Paris',
204
+            lang='fr',
203 205
             do_save=True,
204 206
             do_notify=False,
205 207
         )
@@ -291,6 +293,7 @@ class TestUserRecentlyActiveContentEndpoint(FunctionalTest):
291 293
             name='bob',
292 294
             groups=groups,
293 295
             timezone='Europe/Paris',
296
+            lang='fr',
294 297
             do_save=True,
295 298
             do_notify=False,
296 299
         )
@@ -416,6 +419,7 @@ class TestUserRecentlyActiveContentEndpoint(FunctionalTest):
416 419
             name='bob',
417 420
             groups=groups,
418 421
             timezone='Europe/Paris',
422
+            lang='fr',
419 423
             do_save=True,
420 424
             do_notify=False,
421 425
         )
@@ -704,6 +708,7 @@ class TestUserReadStatusEndpoint(FunctionalTest):
704 708
             name='bob',
705 709
             groups=groups,
706 710
             timezone='Europe/Paris',
711
+            lang='fr',
707 712
             do_save=True,
708 713
             do_notify=False,
709 714
         )
@@ -816,6 +821,7 @@ class TestUserReadStatusEndpoint(FunctionalTest):
816 821
             name='bob',
817 822
             groups=groups,
818 823
             timezone='Europe/Paris',
824
+            lang='fr',
819 825
             do_save=True,
820 826
             do_notify=False,
821 827
         )
@@ -939,6 +945,7 @@ class TestUserReadStatusEndpoint(FunctionalTest):
939 945
             name='bob',
940 946
             groups=groups,
941 947
             timezone='Europe/Paris',
948
+            lang='fr',
942 949
             do_save=True,
943 950
             do_notify=False,
944 951
         )
@@ -1152,6 +1159,7 @@ class TestUserSetContentAsRead(FunctionalTest):
1152 1159
             name='bob',
1153 1160
             groups=groups,
1154 1161
             timezone='Europe/Paris',
1162
+            lang='fr',
1155 1163
             do_save=True,
1156 1164
             do_notify=False,
1157 1165
         )
@@ -1233,6 +1241,7 @@ class TestUserSetContentAsRead(FunctionalTest):
1233 1241
             name='bob',
1234 1242
             groups=groups,
1235 1243
             timezone='Europe/Paris',
1244
+            lang='fr',
1236 1245
             do_save=True,
1237 1246
             do_notify=False,
1238 1247
         )
@@ -1314,6 +1323,7 @@ class TestUserSetContentAsRead(FunctionalTest):
1314 1323
             name='bob',
1315 1324
             groups=groups,
1316 1325
             timezone='Europe/Paris',
1326
+            lang='fr',
1317 1327
             do_save=True,
1318 1328
             do_notify=False,
1319 1329
         )
@@ -1413,6 +1423,7 @@ class TestUserSetContentAsRead(FunctionalTest):
1413 1423
             name='bob',
1414 1424
             groups=groups,
1415 1425
             timezone='Europe/Paris',
1426
+            lang='fr',
1416 1427
             do_save=True,
1417 1428
             do_notify=False,
1418 1429
         )
@@ -1494,6 +1505,7 @@ class TestUserSetContentAsRead(FunctionalTest):
1494 1505
             name='bob',
1495 1506
             groups=groups,
1496 1507
             timezone='Europe/Paris',
1508
+            lang='fr',
1497 1509
             do_save=True,
1498 1510
             do_notify=False,
1499 1511
         )
@@ -1611,6 +1623,7 @@ class TestUserSetContentAsUnread(FunctionalTest):
1611 1623
             name='bob',
1612 1624
             groups=groups,
1613 1625
             timezone='Europe/Paris',
1626
+            lang='fr',
1614 1627
             do_save=True,
1615 1628
             do_notify=False,
1616 1629
         )
@@ -1720,6 +1733,7 @@ class TestUserSetContentAsUnread(FunctionalTest):
1720 1733
             name='bob',
1721 1734
             groups=groups,
1722 1735
             timezone='Europe/Paris',
1736
+            lang='fr',
1723 1737
             do_save=True,
1724 1738
             do_notify=False,
1725 1739
         )
@@ -1801,6 +1815,7 @@ class TestUserSetContentAsUnread(FunctionalTest):
1801 1815
             name='bob',
1802 1816
             groups=groups,
1803 1817
             timezone='Europe/Paris',
1818
+            lang='fr',
1804 1819
             do_save=True,
1805 1820
             do_notify=False,
1806 1821
         )
@@ -1883,6 +1898,7 @@ class TestUserSetContentAsUnread(FunctionalTest):
1883 1898
             name='bob',
1884 1899
             groups=groups,
1885 1900
             timezone='Europe/Paris',
1901
+            lang='fr',
1886 1902
             do_save=True,
1887 1903
             do_notify=False,
1888 1904
         )
@@ -1978,6 +1994,7 @@ class TestUserSetContentAsUnread(FunctionalTest):
1978 1994
             name='bob',
1979 1995
             groups=groups,
1980 1996
             timezone='Europe/Paris',
1997
+            lang='fr',
1981 1998
             do_save=True,
1982 1999
             do_notify=False,
1983 2000
         )
@@ -2122,6 +2139,7 @@ class TestUserSetWorkspaceAsRead(FunctionalTest):
2122 2139
             name='bob',
2123 2140
             groups=groups,
2124 2141
             timezone='Europe/Paris',
2142
+            lang='fr',
2125 2143
             do_save=True,
2126 2144
             do_notify=False,
2127 2145
         )
@@ -2219,6 +2237,7 @@ class TestUserSetWorkspaceAsRead(FunctionalTest):
2219 2237
             name='bob',
2220 2238
             groups=groups,
2221 2239
             timezone='Europe/Paris',
2240
+            lang='fr',
2222 2241
             do_save=True,
2223 2242
             do_notify=False,
2224 2243
         )
@@ -2316,6 +2335,7 @@ class TestUserSetWorkspaceAsRead(FunctionalTest):
2316 2335
             name='bob',
2317 2336
             groups=groups,
2318 2337
             timezone='Europe/Paris',
2338
+            lang='fr',
2319 2339
             do_save=True,
2320 2340
             do_notify=False,
2321 2341
         )
@@ -2496,6 +2516,7 @@ class TestUserEndpoint(FunctionalTest):
2496 2516
             name='bob',
2497 2517
             groups=groups,
2498 2518
             timezone='Europe/Paris',
2519
+            lang='fr',
2499 2520
             do_save=True,
2500 2521
             do_notify=False,
2501 2522
         )
@@ -2523,6 +2544,7 @@ class TestUserEndpoint(FunctionalTest):
2523 2544
         assert res['public_name'] == 'bob'
2524 2545
         assert res['timezone'] == 'Europe/Paris'
2525 2546
         assert res['is_deleted'] is False
2547
+        assert res['lang'] == 'fr'
2526 2548
 
2527 2549
     def test_api__get_user__ok_200__user_itself(self):
2528 2550
         dbsession = get_tm_session(self.session_factory, transaction.manager)
@@ -2546,6 +2568,7 @@ class TestUserEndpoint(FunctionalTest):
2546 2568
             name='bob',
2547 2569
             groups=groups,
2548 2570
             timezone='Europe/Paris',
2571
+            lang='fr',
2549 2572
             do_save=True,
2550 2573
             do_notify=False,
2551 2574
         )
@@ -2605,6 +2628,7 @@ class TestUserEndpoint(FunctionalTest):
2605 2628
             name='bob2',
2606 2629
             groups=groups,
2607 2630
             timezone='Europe/Paris',
2631
+            lang='fr',
2608 2632
             do_save=True,
2609 2633
             do_notify=False,
2610 2634
         )
@@ -2638,6 +2662,7 @@ class TestUserEndpoint(FunctionalTest):
2638 2662
             'password': 'mysuperpassword',
2639 2663
             'profile': 'users',
2640 2664
             'timezone': 'Europe/Paris',
2665
+            'lang': 'fr',
2641 2666
             'public_name': 'test user',
2642 2667
             'email_notification': False,
2643 2668
         }
@@ -2655,7 +2680,7 @@ class TestUserEndpoint(FunctionalTest):
2655 2680
         assert res['email'] == 'test@test.test'
2656 2681
         assert res['public_name'] == 'test user'
2657 2682
         assert res['timezone'] == 'Europe/Paris'
2658
-
2683
+        assert res['lang'] == 'fr'
2659 2684
         dbsession = get_tm_session(self.session_factory, transaction.manager)
2660 2685
         admin = dbsession.query(models.User) \
2661 2686
             .filter(models.User.email == 'admin@admin.admin') \
@@ -2695,6 +2720,7 @@ class TestUserEndpoint(FunctionalTest):
2695 2720
         assert res['email'] == 'test@test.test'
2696 2721
         assert res['public_name'] == 'test'
2697 2722
         assert res['timezone'] == ''
2723
+        assert res['lang'] is None
2698 2724
 
2699 2725
         dbsession = get_tm_session(self.session_factory, transaction.manager)
2700 2726
         admin = dbsession.query(models.User) \
@@ -2731,6 +2757,7 @@ class TestUserEndpoint(FunctionalTest):
2731 2757
             name='bob',
2732 2758
             groups=groups,
2733 2759
             timezone='Europe/Paris',
2760
+            lang='fr',
2734 2761
             do_save=True,
2735 2762
             do_notify=False,
2736 2763
         )
@@ -2748,6 +2775,7 @@ class TestUserEndpoint(FunctionalTest):
2748 2775
             'password': 'mysuperpassword',
2749 2776
             'profile': 'users',
2750 2777
             'timezone': 'Europe/Paris',
2778
+            'lang': 'fr',
2751 2779
             'public_name': 'test user',
2752 2780
             'email_notification': False,
2753 2781
         }
@@ -2779,6 +2807,7 @@ class TestUserEndpoint(FunctionalTest):
2779 2807
             name='bob',
2780 2808
             groups=groups,
2781 2809
             timezone='Europe/Paris',
2810
+            lang='fr',
2782 2811
             do_save=True,
2783 2812
             do_notify=False,
2784 2813
         )
@@ -2797,6 +2826,7 @@ class TestUserEndpoint(FunctionalTest):
2797 2826
             'profile': 'users',
2798 2827
             'timezone': 'Europe/Paris',
2799 2828
             'public_name': 'test user',
2829
+            'lang': 'fr',
2800 2830
             'email_notification': False,
2801 2831
         }
2802 2832
         res = self.testapp.post_json(
@@ -2827,6 +2857,7 @@ class TestUserWithNotificationEndpoint(FunctionalTest):
2827 2857
             'profile': 'users',
2828 2858
             'timezone': 'Europe/Paris',
2829 2859
             'public_name': 'test user',
2860
+            'lang': 'fr',
2830 2861
             'email_notification': True,
2831 2862
         }
2832 2863
         res = self.testapp.post_json(
@@ -2843,6 +2874,7 @@ class TestUserWithNotificationEndpoint(FunctionalTest):
2843 2874
         assert res['email'] == 'test@test.test'
2844 2875
         assert res['public_name'] == 'test user'
2845 2876
         assert res['timezone'] == 'Europe/Paris'
2877
+        assert res['lang'] == 'fr'
2846 2878
 
2847 2879
         dbsession = get_tm_session(self.session_factory, transaction.manager)
2848 2880
         admin = dbsession.query(models.User) \
@@ -2896,6 +2928,7 @@ class TestUserWithNotificationEndpoint(FunctionalTest):
2896 2928
         assert res['email'] == 'test@test.test'
2897 2929
         assert res['public_name'] == 'test'
2898 2930
         assert res['timezone'] == ''
2931
+        assert res['lang'] == None
2899 2932
 
2900 2933
         dbsession = get_tm_session(self.session_factory, transaction.manager)
2901 2934
         admin = dbsession.query(models.User) \
@@ -2944,6 +2977,7 @@ class TestUserWithNotificationEndpoint(FunctionalTest):
2944 2977
             name='bob',
2945 2978
             groups=groups,
2946 2979
             timezone='Europe/Paris',
2980
+            lang='fr',
2947 2981
             do_save=True,
2948 2982
             do_notify=False,
2949 2983
         )
@@ -2998,6 +3032,7 @@ class TestUsersEndpoint(FunctionalTest):
2998 3032
             name='bob',
2999 3033
             groups=groups,
3000 3034
             timezone='Europe/Paris',
3035
+            lang='fr',
3001 3036
             do_save=True,
3002 3037
             do_notify=False,
3003 3038
         )
@@ -3048,6 +3083,7 @@ class TestUsersEndpoint(FunctionalTest):
3048 3083
             name='bob',
3049 3084
             groups=groups,
3050 3085
             timezone='Europe/Paris',
3086
+            lang='fr',
3051 3087
             do_save=True,
3052 3088
             do_notify=False,
3053 3089
         )
@@ -3097,6 +3133,7 @@ class TestKnownMembersEndpoint(FunctionalTest):
3097 3133
             name='bob',
3098 3134
             groups=groups,
3099 3135
             timezone='Europe/Paris',
3136
+            lang='fr',
3100 3137
             do_save=True,
3101 3138
             do_notify=False,
3102 3139
         )
@@ -3106,6 +3143,7 @@ class TestKnownMembersEndpoint(FunctionalTest):
3106 3143
             name='bob2',
3107 3144
             groups=groups,
3108 3145
             timezone='Europe/Paris',
3146
+            lang='fr',
3109 3147
             do_save=True,
3110 3148
             do_notify=False,
3111 3149
         )
@@ -3161,6 +3199,7 @@ class TestKnownMembersEndpoint(FunctionalTest):
3161 3199
             name='bob',
3162 3200
             groups=groups,
3163 3201
             timezone='Europe/Paris',
3202
+            lang='fr',
3164 3203
             do_save=True,
3165 3204
             do_notify=False,
3166 3205
         )
@@ -3170,6 +3209,7 @@ class TestKnownMembersEndpoint(FunctionalTest):
3170 3209
             name='bob2',
3171 3210
             groups=groups,
3172 3211
             timezone='Europe/Paris',
3212
+            lang='fr',
3173 3213
             do_save=True,
3174 3214
             do_notify=False,
3175 3215
         )
@@ -3225,6 +3265,7 @@ class TestKnownMembersEndpoint(FunctionalTest):
3225 3265
             name='bob',
3226 3266
             groups=groups,
3227 3267
             timezone='Europe/Paris',
3268
+            lang='fr',
3228 3269
             do_save=True,
3229 3270
             do_notify=False,
3230 3271
         )
@@ -3234,6 +3275,7 @@ class TestKnownMembersEndpoint(FunctionalTest):
3234 3275
             name='bob2',
3235 3276
             groups=groups,
3236 3277
             timezone='Europe/Paris',
3278
+            lang='fr',
3237 3279
             do_save=True,
3238 3280
             do_notify=False,
3239 3281
         )
@@ -3279,6 +3321,7 @@ class TestKnownMembersEndpoint(FunctionalTest):
3279 3321
             name='bob',
3280 3322
             groups=groups,
3281 3323
             timezone='Europe/Paris',
3324
+            lang='fr',
3282 3325
             do_save=True,
3283 3326
             do_notify=False,
3284 3327
         )
@@ -3288,6 +3331,7 @@ class TestKnownMembersEndpoint(FunctionalTest):
3288 3331
             name='bob2',
3289 3332
             groups=groups,
3290 3333
             timezone='Europe/Paris',
3334
+            lang='fr',
3291 3335
             do_save=True,
3292 3336
             do_notify=False,
3293 3337
         )
@@ -3297,6 +3341,7 @@ class TestKnownMembersEndpoint(FunctionalTest):
3297 3341
             name='bob3',
3298 3342
             groups=groups,
3299 3343
             timezone='Europe/Paris',
3344
+            lang='fr',
3300 3345
             do_save=True,
3301 3346
             do_notify=False,
3302 3347
         )
@@ -3382,6 +3427,7 @@ class TestSetEmailEndpoint(FunctionalTest):
3382 3427
             name='bob',
3383 3428
             groups=groups,
3384 3429
             timezone='Europe/Paris',
3430
+            lang='fr',
3385 3431
             do_save=True,
3386 3432
             do_notify=False,
3387 3433
         )
@@ -3444,6 +3490,7 @@ class TestSetEmailEndpoint(FunctionalTest):
3444 3490
             name='bob',
3445 3491
             groups=groups,
3446 3492
             timezone='Europe/Paris',
3493
+            lang='fr',
3447 3494
             do_save=True,
3448 3495
             do_notify=False,
3449 3496
         )
@@ -3506,6 +3553,7 @@ class TestSetEmailEndpoint(FunctionalTest):
3506 3553
             name='bob',
3507 3554
             groups=groups,
3508 3555
             timezone='Europe/Paris',
3556
+            lang='fr',
3509 3557
             do_save=True,
3510 3558
             do_notify=False,
3511 3559
         )
@@ -3568,6 +3616,7 @@ class TestSetEmailEndpoint(FunctionalTest):
3568 3616
             name='bob',
3569 3617
             groups=groups,
3570 3618
             timezone='Europe/Paris',
3619
+            lang='fr',
3571 3620
             do_save=True,
3572 3621
             do_notify=False,
3573 3622
         )
@@ -3630,6 +3679,7 @@ class TestSetEmailEndpoint(FunctionalTest):
3630 3679
             name='bob',
3631 3680
             groups=groups,
3632 3681
             timezone='Europe/Paris',
3682
+            lang='fr',
3633 3683
             do_save=True,
3634 3684
             do_notify=False,
3635 3685
         )
@@ -3699,6 +3749,7 @@ class TestSetEmailEndpoint(FunctionalTest):
3699 3749
             name='bob',
3700 3750
             groups=groups,
3701 3751
             timezone='Europe/Paris',
3752
+            lang='fr',
3702 3753
             do_save=True,
3703 3754
             do_notify=False,
3704 3755
         )
@@ -3708,6 +3759,7 @@ class TestSetEmailEndpoint(FunctionalTest):
3708 3759
             name='bob2',
3709 3760
             groups=groups,
3710 3761
             timezone='Europe/Paris',
3762
+            lang='fr',
3711 3763
             do_save=True,
3712 3764
             do_notify=False,
3713 3765
         )
@@ -3764,6 +3816,7 @@ class TestSetPasswordEndpoint(FunctionalTest):
3764 3816
             name='bob',
3765 3817
             groups=groups,
3766 3818
             timezone='Europe/Paris',
3819
+            lang='fr',
3767 3820
             do_save=True,
3768 3821
             do_notify=False,
3769 3822
         )
@@ -3826,6 +3879,7 @@ class TestSetPasswordEndpoint(FunctionalTest):
3826 3879
             name='bob',
3827 3880
             groups=groups,
3828 3881
             timezone='Europe/Paris',
3882
+            lang='fr',
3829 3883
             do_save=True,
3830 3884
             do_notify=False,
3831 3885
         )
@@ -3888,6 +3942,7 @@ class TestSetPasswordEndpoint(FunctionalTest):
3888 3942
             name='bob',
3889 3943
             groups=groups,
3890 3944
             timezone='Europe/Paris',
3945
+            lang='fr',
3891 3946
             do_save=True,
3892 3947
             do_notify=False,
3893 3948
         )
@@ -3952,6 +4007,7 @@ class TestSetPasswordEndpoint(FunctionalTest):
3952 4007
             name='bob',
3953 4008
             groups=groups,
3954 4009
             timezone='Europe/Paris',
4010
+            lang='fr',
3955 4011
             do_save=True,
3956 4012
             do_notify=False,
3957 4013
         )
@@ -4013,6 +4069,7 @@ class TestSetPasswordEndpoint(FunctionalTest):
4013 4069
             password='pass',
4014 4070
             name='bob',
4015 4071
             groups=groups,
4072
+            lang='fr',
4016 4073
             timezone='Europe/Paris',
4017 4074
             do_save=True,
4018 4075
             do_notify=False,
@@ -4023,6 +4080,7 @@ class TestSetPasswordEndpoint(FunctionalTest):
4023 4080
             name='bob2',
4024 4081
             groups=groups,
4025 4082
             timezone='Europe/Paris',
4083
+            lang='fr',
4026 4084
             do_save=True,
4027 4085
             do_notify=False,
4028 4086
         )
@@ -4079,6 +4137,7 @@ class TestSetUserInfoEndpoint(FunctionalTest):
4079 4137
             name='bob',
4080 4138
             groups=groups,
4081 4139
             timezone='Europe/Paris',
4140
+            lang='fr',
4082 4141
             do_save=True,
4083 4142
             do_notify=False,
4084 4143
         )
@@ -4102,10 +4161,12 @@ class TestSetUserInfoEndpoint(FunctionalTest):
4102 4161
         assert res['user_id'] == user_id
4103 4162
         assert res['public_name'] == 'bob'
4104 4163
         assert res['timezone'] == 'Europe/Paris'
4164
+        assert res['lang'] == 'fr'
4105 4165
         # Set params
4106 4166
         params = {
4107 4167
             'public_name': 'updated',
4108 4168
             'timezone': 'Europe/London',
4169
+            'lang': 'en',
4109 4170
         }
4110 4171
         self.testapp.put_json(
4111 4172
             '/api/v2/users/{}'.format(user_id),
@@ -4121,6 +4182,7 @@ class TestSetUserInfoEndpoint(FunctionalTest):
4121 4182
         assert res['user_id'] == user_id
4122 4183
         assert res['public_name'] == 'updated'
4123 4184
         assert res['timezone'] == 'Europe/London'
4185
+        assert res['lang'] == 'en'
4124 4186
 
4125 4187
     def test_api__set_user_info__ok_200__user_itself(self):
4126 4188
         dbsession = get_tm_session(self.session_factory, transaction.manager)
@@ -4144,6 +4206,7 @@ class TestSetUserInfoEndpoint(FunctionalTest):
4144 4206
             name='bob',
4145 4207
             groups=groups,
4146 4208
             timezone='Europe/Paris',
4209
+            lang='fr',
4147 4210
             do_save=True,
4148 4211
             do_notify=False,
4149 4212
         )
@@ -4167,10 +4230,12 @@ class TestSetUserInfoEndpoint(FunctionalTest):
4167 4230
         assert res['user_id'] == user_id
4168 4231
         assert res['public_name'] == 'bob'
4169 4232
         assert res['timezone'] == 'Europe/Paris'
4233
+        assert res['lang'] == 'fr'
4170 4234
         # Set params
4171 4235
         params = {
4172 4236
             'public_name': 'updated',
4173 4237
             'timezone': 'Europe/London',
4238
+            'lang' : 'en',
4174 4239
         }
4175 4240
         self.testapp.put_json(
4176 4241
             '/api/v2/users/{}'.format(user_id),
@@ -4186,6 +4251,7 @@ class TestSetUserInfoEndpoint(FunctionalTest):
4186 4251
         assert res['user_id'] == user_id
4187 4252
         assert res['public_name'] == 'updated'
4188 4253
         assert res['timezone'] == 'Europe/London'
4254
+        assert res['lang'] == 'en'
4189 4255
 
4190 4256
     def test_api__set_user_email__err_403__other_normal_user(self):
4191 4257
         dbsession = get_tm_session(self.session_factory, transaction.manager)
@@ -4209,6 +4275,7 @@ class TestSetUserInfoEndpoint(FunctionalTest):
4209 4275
             name='bob',
4210 4276
             groups=groups,
4211 4277
             timezone='Europe/Paris',
4278
+            lang='fr',
4212 4279
             do_save=True,
4213 4280
             do_notify=False,
4214 4281
         )
@@ -4218,6 +4285,7 @@ class TestSetUserInfoEndpoint(FunctionalTest):
4218 4285
             name='test',
4219 4286
             groups=groups,
4220 4287
             timezone='Europe/Paris',
4288
+            lang='fr',
4221 4289
             do_save=True,
4222 4290
             do_notify=False,
4223 4291
         )
@@ -4237,6 +4305,7 @@ class TestSetUserInfoEndpoint(FunctionalTest):
4237 4305
         params = {
4238 4306
             'public_name': 'updated',
4239 4307
             'timezone': 'Europe/London',
4308
+            'lang': 'en'
4240 4309
         }
4241 4310
         self.testapp.put_json(
4242 4311
             '/api/v2/users/{}'.format(user_id),
@@ -4274,6 +4343,7 @@ class TestSetUserProfilEndpoint(FunctionalTest):
4274 4343
             name='bob',
4275 4344
             groups=groups,
4276 4345
             timezone='Europe/Paris',
4346
+            lang='fr',
4277 4347
             do_save=True,
4278 4348
             do_notify=False,
4279 4349
         )
@@ -4336,6 +4406,7 @@ class TestSetUserProfilEndpoint(FunctionalTest):
4336 4406
             name='bob',
4337 4407
             groups=groups,
4338 4408
             timezone='Europe/Paris',
4409
+            lang='fr',
4339 4410
             do_save=True,
4340 4411
             do_notify=False,
4341 4412
         )
@@ -4398,6 +4469,7 @@ class TestSetUserProfilEndpoint(FunctionalTest):
4398 4469
             name='bob',
4399 4470
             groups=groups,
4400 4471
             timezone='Europe/Paris',
4472
+            lang='fr',
4401 4473
             do_save=True,
4402 4474
             do_notify=False,
4403 4475
         )
@@ -4407,6 +4479,7 @@ class TestSetUserProfilEndpoint(FunctionalTest):
4407 4479
             name='test',
4408 4480
             groups=groups,
4409 4481
             timezone='Europe/Paris',
4482
+            lang='fr',
4410 4483
             do_save=True,
4411 4484
             do_notify=False,
4412 4485
         )
@@ -4463,6 +4536,7 @@ class TestSetUserEnableDisableEndpoints(FunctionalTest):
4463 4536
             name='bob',
4464 4537
             groups=groups,
4465 4538
             timezone='Europe/Paris',
4539
+            lang='fr',
4466 4540
             do_save=True,
4467 4541
             do_notify=False,
4468 4542
         )
@@ -4521,6 +4595,7 @@ class TestSetUserEnableDisableEndpoints(FunctionalTest):
4521 4595
             name='bob',
4522 4596
             groups=groups,
4523 4597
             timezone='Europe/Paris',
4598
+            lang='fr',
4524 4599
             do_save=True,
4525 4600
             do_notify=False,
4526 4601
         )
@@ -4579,6 +4654,7 @@ class TestSetUserEnableDisableEndpoints(FunctionalTest):
4579 4654
             name='bob',
4580 4655
             groups=groups,
4581 4656
             timezone='Europe/Paris',
4657
+            lang='fr',
4582 4658
             do_save=True,
4583 4659
             do_notify=False,
4584 4660
         )
@@ -4588,6 +4664,7 @@ class TestSetUserEnableDisableEndpoints(FunctionalTest):
4588 4664
             name='test2',
4589 4665
             groups=groups,
4590 4666
             timezone='Europe/Paris',
4667
+            lang='fr',
4591 4668
             do_save=True,
4592 4669
             do_notify=False,
4593 4670
         )
@@ -4631,6 +4708,7 @@ class TestSetUserEnableDisableEndpoints(FunctionalTest):
4631 4708
             name='bob',
4632 4709
             groups=groups,
4633 4710
             timezone='Europe/Paris',
4711
+            lang='fr',
4634 4712
             do_save=True,
4635 4713
             do_notify=False,
4636 4714
         )
@@ -4640,6 +4718,7 @@ class TestSetUserEnableDisableEndpoints(FunctionalTest):
4640 4718
             name='test2',
4641 4719
             groups=groups,
4642 4720
             timezone='Europe/Paris',
4721
+            lang='fr',
4643 4722
             do_save=True,
4644 4723
             do_notify=False,
4645 4724
         )
@@ -4683,6 +4762,7 @@ class TestSetUserEnableDisableEndpoints(FunctionalTest):
4683 4762
             name='bob',
4684 4763
             groups=groups,
4685 4764
             timezone='Europe/Paris',
4765
+            lang='fr',
4686 4766
             do_save=True,
4687 4767
             do_notify=False,
4688 4768
         )

+ 2 - 0
backend/tracim_backend/tests/library/test_user_api.py Visa fil

@@ -55,6 +55,7 @@ class TestUserApi(DefaultTest):
55 55
             password='pass',
56 56
             name='bob',
57 57
             timezone='+2',
58
+            lang='en',
58 59
             do_save=True,
59 60
             do_notify=False,
60 61
         )
@@ -63,6 +64,7 @@ class TestUserApi(DefaultTest):
63 64
         assert u.validate_password('pass')
64 65
         assert u.display_name == 'bob'
65 66
         assert u.timezone == '+2'
67
+        assert u.lang == 'en'
66 68
 
67 69
     def test_unit__user_with_email_exists__ok__nominal_case(self):
68 70
         api = UserApi(

+ 27 - 0
backend/tracim_backend/views/core_api/schemas.py Visa fil

@@ -84,6 +84,7 @@ class UserSchema(UserDigestSchema):
84 84
     )
85 85
     # TODO - G.M - 17-04-2018 - Restrict timezone values
86 86
     timezone = marshmallow.fields.String(
87
+        description="Timezone as tz database format",
87 88
         example="Europe/Paris",
88 89
     )
89 90
     # TODO - G.M - 17-04-2018 - check this, relative url allowed ?
@@ -99,6 +100,14 @@ class UserSchema(UserDigestSchema):
99 100
         validate=OneOf(Profile._NAME),
100 101
         example='managers',
101 102
     )
103
+    lang = marshmallow.fields.String(
104
+        description="User langage in iso639 format",
105
+        example='en',
106
+        required=False,
107
+        validate=Length(min=2, max=3),
108
+        allow_none=True,
109
+        default=None,
110
+    )
102 111
 
103 112
     class Meta:
104 113
         description = 'User account of Tracim'
@@ -138,6 +147,7 @@ class SetPasswordSchema(LoggedInUserPasswordSchema):
138 147
 
139 148
 class UserInfosSchema(marshmallow.Schema):
140 149
     timezone = marshmallow.fields.String(
150
+        description="Timezone as tz database format",
141 151
         example="Europe/Paris",
142 152
         required=True,
143 153
     )
@@ -145,6 +155,14 @@ class UserInfosSchema(marshmallow.Schema):
145 155
         example='Suri Cate',
146 156
         required=True,
147 157
     )
158
+    lang = marshmallow.fields.String(
159
+        description="User langage in iso639 format",
160
+        example='en',
161
+        required=True,
162
+        validate=Length(min=2, max=3),
163
+        allow_none=True,
164
+        default=None,
165
+    )
148 166
 
149 167
     @post_load
150 168
     def create_user_info_object(self, data):
@@ -179,6 +197,7 @@ class UserCreationSchema(marshmallow.Schema):
179 197
         default=Group.TIM_USER_GROUPNAME
180 198
     )
181 199
     timezone = marshmallow.fields.String(
200
+        description="Timezone as tz database format",
182 201
         example="Europe/Paris",
183 202
         required=False,
184 203
         default=''
@@ -188,6 +207,14 @@ class UserCreationSchema(marshmallow.Schema):
188 207
         required=False,
189 208
         default=None,
190 209
     )
210
+    lang = marshmallow.fields.String(
211
+        description="User langage in iso639 format",
212
+        example='en',
213
+        required=False,
214
+        validate=Length(min=2, max=3),
215
+        allow_none=True,
216
+        default=None,
217
+    )
191 218
     email_notification = marshmallow.fields.Bool(
192 219
         example=True,
193 220
         required=False,

+ 2 - 0
backend/tracim_backend/views/core_api/user_controller.py Visa fil

@@ -190,6 +190,7 @@ class UserController(Controller):
190 190
             request.candidate_user,
191 191
             name=hapic_data.body.public_name,
192 192
             timezone=hapic_data.body.timezone,
193
+            lang=hapic_data.body.lang,
193 194
             do_save=True
194 195
         )
195 196
         return uapi.get_user_with_context(user)
@@ -219,6 +220,7 @@ class UserController(Controller):
219 220
             email=hapic_data.body.email,
220 221
             password=hapic_data.body.password,
221 222
             timezone=hapic_data.body.timezone,
223
+            lang=hapic_data.body.lang,
222 224
             name=hapic_data.body.public_name,
223 225
             do_notify=hapic_data.body.email_notification,
224 226
             groups=groups,