Browse Source

Merge branch 'fix/better_api_doc' of github.com:tracim/tracim_backend into fix/few_more_tests

Guénaël Muller 6 years ago
parent
commit
3eba2081c1

+ 0 - 1
README.md View File

@@ -124,7 +124,6 @@ You can run it this way with docker :
124 124
     docker pull mailhog/mailhog
125 125
     docker run -d -p 1025:1025 -p 8025:8025 mailhog/mailhog
126 126
 
127
-
128 127
 Run your project's tests:
129 128
 
130 129
     pytest

+ 275 - 0
development.ini.oloool View File

@@ -0,0 +1,275 @@
1
+###
2
+# app configuration
3
+# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
4
+###
5
+;[pipeline:main]
6
+;pipeline = tracim_web
7
+
8
+[app:main]
9
+use = egg:tracim_backend
10
+
11
+pyramid.reload_templates = true
12
+pyramid.debug_authorization = false
13
+pyramid.debug_notfound = false
14
+pyramid.debug_routematch = false
15
+pyramid.default_locale_name = en
16
+;pyramid.includes =
17
+;    pyramid_debugtoolbar
18
+
19
+retry.attempts = 3
20
+
21
+sqlalchemy.url = sqlite:///%(here)s/tracim.sqlite
22
+
23
+# By default, the toolbar only appears for clients from IP addresses
24
+# '127.0.0.1' and '::1'.
25
+# debugtoolbar.hosts = 127.0.0.1 ::1
26
+
27
+###
28
+# TRACIM SPECIFIC CONF
29
+###
30
+
31
+### Global
32
+
33
+cache_dir = %(here)s/data
34
+# preview generator cache directory
35
+preview_cache_dir = /tmp/tracim/preview/
36
+# file depot storage
37
+depot_storage_name = tracim
38
+depot_storage_dir = %(here)s/depot/
39
+
40
+# The following parameters allow to personalize the home page
41
+# They are html ready (you can put html tags they will be interpreted)
42
+website.title = TRACIM
43
+website.title.color = #555
44
+website.home.subtitle = Default login: email: admin@admin.admin (password: admin@admin.admin)
45
+website.home.tag_line = <div class="text-center" style="font-weight: bold;">Collaboration, versionning and traceability</div>
46
+website.home.below_login_form = in case of problem, please contact the administrator.
47
+# Values may be 'all' or 'folders'
48
+website.treeview.content = all
49
+# The following base_url is used for links and icons
50
+# integrated in the email notifcations
51
+website.base_url = http://127.0.0.1:8080
52
+# If config not provided, it will be extracted from website.base_url
53
+website.server_name = 127.0.0.1
54
+
55
+# Specifies if the update of comments and attached files is allowed (by the owner only).
56
+# Examples:
57
+#    600 means 10 minutes (ie 600 seconds)
58
+#   3600 means 1 hour (60x60 seconds)
59
+#
60
+# Allowed values:
61
+#  -1 means that content update is allowed for ever
62
+#   0 means that content update is not allowed
63
+#   x means that content update is allowed for x seconds (with x>0)
64
+content.update.allowed.duration = 3600
65
+
66
+# Auth type (internal or ldap)
67
+auth_type = internal
68
+# If auth_type is ldap, uncomment following ldap_* parameters
69
+# LDAP server address
70
+# ldap_url = ldap://localhost:389
71
+# Base dn to make queries
72
+# ldap_base_dn = dc=directory,dc=fsf,dc=org
73
+# Bind dn to identify the search
74
+# ldap_bind_dn = cn=admin,dc=directory,dc=fsf,dc=org
75
+# The bind password
76
+# ldap_bind_pass = toor
77
+# Attribute name of user record who contain user login (email)
78
+# ldap_ldap_naming_attribute = uid
79
+# Matching between ldap attribute and ldap user field (ldap_attr1=user_field1,ldap_attr2=user_field2,...)
80
+# ldap_user_attributes = mail=email
81
+# TLS usage to communicate with your LDAP server
82
+# ldap_tls = False
83
+# If True, LDAP own tracim group managment (not available for now!)
84
+# ldap_group_enabled = False
85
+# User auth token validity in seconds (used to interfaces like web calendars)
86
+user.auth_token.validity = 604800
87
+
88
+### Mail
89
+
90
+# Reset password through email related configuration.
91
+# These emails will be sent through SMTP
92
+#
93
+resetpassword.email_sender = email@sender.com
94
+resetpassword.smtp_host = smtp.sender
95
+resetpassword.smtp_port = 25
96
+resetpassword.smtp_login = smtp.login
97
+resetpassword.smtp_passwd = smtp.password
98
+
99
+email.notification.activated = false
100
+# email.notification.log_file_path = /tmp/mail-notifications.log
101
+# email notifications can be sent with the user_id added as an identifier
102
+# this way email clients like Thunderbird will be able to distinguish
103
+# notifications generated by a user or another one
104
+email.notification.from.email = dev.tracim.maildaemon+{user_id}@algoo.fr
105
+email.notification.from.default_label = Tracim Notifications
106
+email.notification.reply_to.email = dev.tracim.maildaemon+{content_id}@algoo.fr
107
+email.notification.references.email = dev.tracim.maildaemon+{content_id}@algoo.fr
108
+email.notification.content_update.template.html = %(here)s/tracim/templates/mail/content_update_body_html.mak
109
+email.notification.content_update.template.text = %(here)s/tracim/templates/mail/content_update_body_text.mak
110
+email.notification.created_account.template.html = %(here)s/tracim/templates/mail/created_account_body_html.mak
111
+email.notification.created_account.template.text = %(here)s/tracim/templates/mail/created_account_body_text.mak
112
+# Note: items between { and } are variable names. Do not remove / rename them
113
+email.notification.content_update.subject = [{website_title}] [{workspace_label}] {content_label} ({content_status_label})
114
+email.notification.created_account.subject = [{website_title}] Created account
115
+# processing_mode may be sync or async
116
+email.notification.processing_mode = async
117
+email.notification.smtp.server = mail.gandi.net
118
+email.notification.smtp.port = 25
119
+email.notification.smtp.user = dev.tracim.maildaemon@algoo.fr
120
+email.notification.smtp.password = dev.tracim.maildaemon
121
+
122
+## Email sending configuration
123
+# processing_mode may be sync or async,
124
+# with async, please configure redis below
125
+email.processing_mode = sync
126
+# email.async.redis.host = localhost
127
+# email.async.redis.port = 6379
128
+# email.async.redis.db = 0
129
+
130
+# Email reply configuration
131
+email.reply.activated = false
132
+email.reply.imap.server = your_imap_server
133
+email.reply.imap.port = 993
134
+email.reply.imap.user = your_imap_user
135
+email.reply.imap.password = your_imap_password
136
+email.reply.imap.folder = INBOX
137
+email.reply.imap.use_ssl = true
138
+email.reply.imap.use_idle = true
139
+# Re-new connection each 10 minutes
140
+email.reply.connection.max_lifetime = 600
141
+# Token for communication between mail fetcher and tracim controller
142
+email.reply.token = mysecuretoken
143
+# Delay in seconds between each check
144
+email.reply.check.heartbeat = 60
145
+email.reply.use_html_parsing = true
146
+email.reply.use_txt_parsing = true
147
+# Lockfile path is required for email_reply feature,
148
+# it's just an empty file use to prevent concurrent access to imap unseen mail
149
+email.reply.lockfile_path = %(here)s/email_fetcher.lock
150
+
151
+### Radical (CalDav server) configuration
152
+
153
+# radicale.server.host = 0.0.0.0
154
+# radicale.server.port = 5232
155
+# radicale.server.ssl = false
156
+radicale.server.filesystem.folder = %(here)s/radicale/collections/
157
+# radicale.server.allow_origin = *
158
+# radicale.server.realm_message = Tracim Calendar - Password Required
159
+## url can be extended like http://127.0.0.1:5232/calendar
160
+## in this case, you have to create your own proxy behind this url.
161
+## and update following parameters
162
+# radicale.client.base_url.host = http://127.0.0.1:5232
163
+# radicale.client.base_url.prefix = /
164
+
165
+### WSGIDAV
166
+
167
+wsgidav.config_path = %(here)s/wsgidav.conf
168
+## url can be extended like 127.0.0.1/webdav
169
+## in this case, you have to create your own proxy behind this url.
170
+## Do not set http:// prefix.
171
+# wsgidav.client.base_url = 127.0.0.1:<WSGIDAV_PORT>
172
+
173
+###
174
+# wsgi server configuration
175
+###
176
+[uwsgi]
177
+# Legacy server config (waitress)
178
+[server:main]
179
+use = egg:waitress#main
180
+listen = localhost:6543
181
+
182
+[alembic]
183
+# path to migration scripts
184
+script_location = tracim/migration
185
+
186
+# template used to generate migration files
187
+# file_template = %%(rev)s_%%(slug)s
188
+
189
+# timezone to use when rendering the date
190
+# within the migration file as well as the filename.
191
+# string value is passed to dateutil.tz.gettz()
192
+# leave blank for localtime
193
+# timezone =
194
+
195
+# max length of characters to apply to the
196
+# "slug" field
197
+#truncate_slug_length = 40
198
+
199
+# set to 'true' to run the environment during
200
+# the 'revision' command, regardless of autogenerate
201
+# revision_environment = false
202
+
203
+# set to 'true' to allow .pyc and .pyo files without
204
+# a source .py file to be detected as revisions in the
205
+# versions/ directory
206
+# sourceless = false
207
+
208
+# version location specification; this defaults
209
+# to migrate/versions.  When using multiple version
210
+# directories, initial revisions must be specified with --version-path
211
+# version_locations = %(here)s/bar %(here)s/bat migrate/versions
212
+
213
+# the output encoding used when revision files
214
+# are written from script.py.mako
215
+# output_encoding = utf-8
216
+
217
+sqlalchemy.url = sqlite:///%(here)s/tracim.sqlite
218
+
219
+###
220
+# logging configuration
221
+# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
222
+###
223
+
224
+[loggers]
225
+keys = root, tracim, sqlalchemy, alembic, sentry
226
+
227
+[handlers]
228
+keys = console, sentry
229
+
230
+[formatters]
231
+keys = generic
232
+
233
+[logger_root]
234
+level = INFO
235
+handlers = console, sentry
236
+
237
+[logger_sentry]
238
+level = WARN
239
+handlers = console
240
+qualname = sentry.errors
241
+propagate = 0
242
+
243
+[logger_tracim]
244
+level = DEBUG
245
+handlers =
246
+qualname = tracim
247
+
248
+[logger_sqlalchemy]
249
+level = INFO
250
+handlers =
251
+qualname = sqlalchemy.engine
252
+# "level = INFO" logs SQL queries.
253
+# "level = DEBUG" logs SQL queries and results.
254
+# "level = WARN" logs neither.  (Recommended for production systems.)
255
+
256
+[logger_alembic]
257
+level = INFO
258
+handlers =
259
+qualname = alembic
260
+
261
+[handler_console]
262
+class = StreamHandler
263
+args = (sys.stderr,)
264
+level = NOTSET
265
+formatter = generic
266
+
267
+[handler_sentry]
268
+class = raven.handlers.logging.SentryHandler
269
+args = ('http://1dbab0942cca4fbb97f3dae62cbc965d:4d1deecd8abc41e38c02b37ed4954f58@127.0.0.1:9000/4',)
270
+level = WARNING
271
+formatter = generic
272
+
273
+[formatter_generic]
274
+format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
275
+datefmt = %H:%M:%S

+ 1 - 1
setup.py View File

@@ -34,12 +34,12 @@ requires = [
34 34
     # others
35 35
     'filedepot',
36 36
     'babel',
37
+    'python-slugify',
37 38
     # mail-notifier
38 39
     'mako',
39 40
     'lxml',
40 41
     'redis',
41 42
     'rq',
42
-    'python-slugify',
43 43
 ]
44 44
 
45 45
 tests_require = [

+ 1 - 1
tracim/exceptions.py View File

@@ -57,7 +57,7 @@ class SameValueError(ValueError):
57 57
     pass
58 58
 
59 59
 
60
-class NotAuthentificated(TracimException):
60
+class NotAuthenticated(TracimException):
61 61
     pass
62 62
 
63 63
 

+ 3 - 1
tracim/lib/core/userworkspace.py View File

@@ -49,6 +49,7 @@ class RoleApi(object):
49 49
         """
50 50
         Return WorkspaceInContext object from Workspace
51 51
         """
52
+        assert self._config
52 53
         workspace = UserRoleWorkspaceInContext(
53 54
             user_role=user_role,
54 55
             dbsession=self._session,
@@ -138,7 +139,8 @@ class RoleApi(object):
138 139
         workspace:Workspace
139 140
     ) -> typing.List[UserRoleInWorkspace]:
140 141
         return self._session.query(UserRoleInWorkspace)\
141
-            .filter(UserRoleInWorkspace.workspace_id == workspace.workspace_id).all()  # nopep8
142
+            .filter(UserRoleInWorkspace.workspace_id==workspace.workspace_id)\
143
+            .all()
142 144
 
143 145
     def save(self, role: UserRoleInWorkspace) -> None:
144 146
         self._session.flush()

+ 35 - 21
tracim/lib/mail_notifier/notifier.py View File

@@ -238,7 +238,7 @@ class EmailManager(object):
238 238
         notifiable_roles = WorkspaceApi(
239 239
             current_user=user,
240 240
             session=self.session,
241
-            config=self.config
241
+            config=self.config,
242 242
         ).get_notifiable_roles(content.workspace)
243 243
 
244 244
         if len(notifiable_roles) <= 0:
@@ -358,22 +358,22 @@ class EmailManager(object):
358 358
         text_template_file_path = self.config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_TEXT  # nopep8
359 359
         html_template_file_path = self.config.EMAIL_NOTIFICATION_CREATED_ACCOUNT_TEMPLATE_HTML  # nopep8
360 360
 
361
+        context = {
362
+            'user': user,
363
+            'password': password,
364
+            # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for logo_url  # nopep8
365
+            'logo_url': '',
366
+            # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for login_url  # nopep8
367
+            'login_url': self.config.WEBSITE_BASE_URL,
368
+        }
361 369
         body_text = self._render_template(
362 370
             mako_template_filepath=text_template_file_path,
363
-            context={
364
-                'user': user,
365
-                'password': password,
366
-                'login_url': self.config.WEBSITE_BASE_URL,
367
-            }
371
+            context=context
368 372
         )
369 373
 
370 374
         body_html = self._render_template(
371 375
             mako_template_filepath=html_template_file_path,
372
-            context={
373
-                'user': user,
374
-                'password': password,
375
-                'login_url': self.config.WEBSITE_BASE_URL,
376
-            }
376
+            context=context,
377 377
         )
378 378
 
379 379
         part1 = MIMEText(body_text, 'plain', 'utf-8')
@@ -434,6 +434,15 @@ class EmailManager(object):
434 434
         content_text = ''
435 435
         call_to_action_text = ''
436 436
 
437
+        # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for call_to_action_url  # nopep8
438
+        call_to_action_url =''
439
+        # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for status_icon_url  # nopep8
440
+        status_icon_url = ''
441
+        # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for workspace_url  # nopep8
442
+        workspace_url = ''
443
+        # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for logo_url  # nopep8
444
+        logo_url = ''
445
+
437 446
         action = content.get_last_action().id
438 447
         if ActionDescription.COMMENT == action:
439 448
             content_intro = l_('<span id="content-intro-username">{}</span> added a comment:').format(actor.display_name)
@@ -523,20 +532,25 @@ class EmailManager(object):
523 532
             )
524 533
             raise ValueError('Unexpected empty notification')
525 534
 
535
+        context = {
536
+            'user': role.user,
537
+            'workspace': role.workspace,
538
+            'workspace_url': workspace_url,
539
+            'main_title': main_title,
540
+            'status_label': content.get_status().label,
541
+            'status_icon_url': status_icon_url,
542
+            'role_label': role.role_as_label(),
543
+            'content_intro': content_intro,
544
+            'content_text': content_text,
545
+            'call_to_action_text': call_to_action_text,
546
+            'call_to_action_url': call_to_action_url,
547
+            'logo_url': logo_url,
548
+        }
526 549
         user = role.user
527 550
         workspace = role.workspace
528 551
         body_content = self._render_template(
529 552
             mako_template_filepath=mako_template_filepath,
530
-            context={
531
-                'user': role.user,
532
-                'workspace': role.workspace,
533
-                'main_title': main_title,
534
-                'status': content.get_status().label,
535
-                'role': role.role_as_label(),
536
-                'content_intro': content_intro,
537
-                'content_text': content_text,
538
-                'call_to_action_text': call_to_action_text,
539
-            }
553
+            context=context,
540 554
         )
541 555
         return body_content
542 556
 

+ 1 - 1
tracim/lib/utils/authentification.py View File

@@ -51,6 +51,6 @@ def _get_basic_auth_unsafe_user(
51 51
         if not login:
52 52
             return None
53 53
         user = uapi.get_one_by_email(login)
54
-    except (NoResultFound, UserDoesNotExist):
54
+    except UserDoesNotExist:
55 55
         return None
56 56
     return user

+ 3 - 3
tracim/lib/utils/authorization.py View File

@@ -44,7 +44,7 @@ class AcceptAllAuthorizationPolicy(object):
44 44
 # We prefer to use decorators
45 45
 
46 46
 
47
-def require_same_user_or_profile(group):
47
+def require_same_user_or_profile(group: int):
48 48
     """
49 49
     Decorator for view to restrict access of tracim request if candidate user
50 50
     is distinct from authenticated user and not with high enough profile.
@@ -64,7 +64,7 @@ def require_same_user_or_profile(group):
64 64
     return decorator
65 65
 
66 66
 
67
-def require_profile(group):
67
+def require_profile(group: int):
68 68
     """
69 69
     Decorator for view to restrict access of tracim request if profile is
70 70
     not high enough
@@ -82,7 +82,7 @@ def require_profile(group):
82 82
     return decorator
83 83
 
84 84
 
85
-def require_workspace_role(minimal_required_role):
85
+def require_workspace_role(minimal_required_role: int):
86 86
     """
87 87
     Decorator for view to restrict access of tracim request if role
88 88
     is not high enough

+ 6 - 3
tracim/lib/utils/request.py View File

@@ -6,7 +6,7 @@ from pyramid.request import Request
6 6
 from sqlalchemy.orm.exc import NoResultFound
7 7
 
8 8
 
9
-from tracim.exceptions import NotAuthentificated
9
+from tracim.exceptions import NotAuthenticated
10 10
 from tracim.exceptions import UserNotFoundInTracimRequest
11 11
 from tracim.exceptions import UserDoesNotExist
12 12
 from tracim.exceptions import WorkspaceNotFound
@@ -40,11 +40,14 @@ class TracimRequest(Request):
40 40
         )
41 41
         # Current workspace, found by request headers or content
42 42
         self._current_workspace = None  # type: Workspace
43
+
43 44
         # Authenticated user
44 45
         self._current_user = None  # type: User
46
+
45 47
         # User found from request headers, content, distinct from authenticated
46 48
         # user
47 49
         self._user_candidate = None  # type: User
50
+
48 51
         # INFO - G.M - 18-05-2018 - Close db at the end of the request
49 52
         self.add_finished_callback(self._cleanup)
50 53
 
@@ -168,7 +171,7 @@ def get_auth_safe_user(
168 171
             raise UserNotFoundInTracimRequest('You request a current user but the context not permit to found one')  # nopep8
169 172
         user = uapi.get_one_by_email(login)
170 173
     except (UserDoesNotExist, UserNotFoundInTracimRequest) as exc:
171
-        raise NotAuthentificated('User {} not found'.format(login)) from exc
174
+        raise NotAuthenticated('User {} not found'.format(login)) from exc
172 175
     return user
173 176
 
174 177
 
@@ -187,7 +190,7 @@ def get_workspace(
187 190
         if 'workspace_id' in request.matchdict:
188 191
             workspace_id = request.matchdict['workspace_id']
189 192
         if not workspace_id:
190
-            raise WorkspaceNotFound('No workspace_id param')
193
+            raise WorkspaceNotFound('No workspace_id property found in request')
191 194
         wapi = WorkspaceApi(
192 195
             current_user=user,
193 196
             session=request.dbsession,

+ 2 - 2
tracim/lib/webdav/design.py View File

@@ -164,7 +164,7 @@ def designPage(content: data.Content, content_revision: data.ContentRevisionRO)
164 164
                     <td>%s</td>
165 165
                 </tr>
166 166
                 ''' % ('warning' if event.id == content_revision.revision_id else '',
167
-                       event.type.icon,
167
+                       event.type.fa_icon,
168 168
                        label,
169 169
                        date,
170 170
                        event.owner.display_name,
@@ -282,7 +282,7 @@ def designThread(content: data.Content, content_revision: data.ContentRevisionRO
282 282
                         </div>
283 283
                     </div>
284 284
                     ''' % ('warning' if t.id == content_revision.revision_id else '',
285
-                           t.type.icon,
285
+                           t.type.fa_icon,
286 286
                            t.owner.display_name,
287 287
                            t.create_readable_date(),
288 288
                            label,

+ 21 - 9
tracim/models/applications.py View File

@@ -10,15 +10,27 @@ class Application(object):
10 10
             self,
11 11
             label: str,
12 12
             slug: str,
13
-            icon: str,
13
+            fa_icon: str,
14 14
             hexcolor: str,
15 15
             is_active: bool,
16 16
             config: typing.Dict[str, str],
17 17
             main_route: str,
18 18
     ) -> None:
19
+        """
20
+        @param label: public label of application
21
+        @param slug: identifier of application
22
+        @param icon: font awesome icon class
23
+        @param hexcolor: hexa color of application main color
24
+        @param is_active: True if application enable, False if inactive
25
+        @param config: a dict with eventual application config
26
+        @param main_route: the route of the frontend "home" screen of
27
+        the application. For exemple, if you have an application
28
+        called "calendar", the main route will be something
29
+        like /#/workspace/{wid}/calendar.
30
+        """
19 31
         self.label = label
20 32
         self.slug = slug
21
-        self.icon = icon
33
+        self.fa_icon = fa_icon
22 34
         self.hexcolor = hexcolor
23 35
         self.is_active = is_active
24 36
         self.config = config
@@ -29,7 +41,7 @@ class Application(object):
29 41
 calendar = Application(
30 42
     label='Calendar',
31 43
     slug='calendar',
32
-    icon='calendar-alt',
44
+    fa_icon='calendar-alt',
33 45
     hexcolor='#757575',
34 46
     is_active=True,
35 47
     config={},
@@ -39,7 +51,7 @@ calendar = Application(
39 51
 thread = Application(
40 52
     label='Threads',
41 53
     slug='contents/threads',
42
-    icon='comments-o',
54
+    fa_icon='comments-o',
43 55
     hexcolor='#ad4cf9',
44 56
     is_active=True,
45 57
     config={},
@@ -47,10 +59,10 @@ thread = Application(
47 59
 
48 60
 )
49 61
 
50
-file = Application(
62
+_file = Application(
51 63
     label='Files',
52 64
     slug='contents/files',
53
-    icon='paperclip',
65
+    fa_icon='paperclip',
54 66
     hexcolor='#FF9900',
55 67
     is_active=True,
56 68
     config={},
@@ -60,7 +72,7 @@ file = Application(
60 72
 markdownpluspage = Application(
61 73
     label='Markdown Plus Documents',  # TODO - G.M - 24-05-2018 - Check label
62 74
     slug='contents/markdownpluspage',
63
-    icon='file-code',
75
+    fa_icon='file-code',
64 76
     hexcolor='#f12d2d',
65 77
     is_active=True,
66 78
     config={},
@@ -70,7 +82,7 @@ markdownpluspage = Application(
70 82
 htmlpage = Application(
71 83
     label='Text Documents',  # TODO - G.M - 24-05-2018 - Check label
72 84
     slug='contents/htmlpage',
73
-    icon='file-text-o',
85
+    fa_icon='file-text-o',
74 86
     hexcolor='#3f52e3',
75 87
     is_active=True,
76 88
     config={},
@@ -81,7 +93,7 @@ htmlpage = Application(
81 93
 applications = [
82 94
     htmlpage,
83 95
     markdownpluspage,
84
-    file,
96
+    _file,
85 97
     thread,
86 98
     calendar,
87 99
 ]

+ 9 - 8
tracim/models/data.py View File

@@ -162,7 +162,7 @@ class UserRoleInWorkspace(DeclarativeBase):
162 162
     #
163 163
     #
164 164
     # @property
165
-    # def icon(self):
165
+    # def fa_icon(self):
166 166
     #     return UserRoleInWorkspace.ICON[self.role]
167 167
     #
168 168
     # @property
@@ -200,7 +200,7 @@ class RoleType(object):
200 200
     def __init__(self, role_id):
201 201
         self.role_type_id = role_id
202 202
         # TODO - G.M - 10-04-2018 - [Cleanup] Drop this
203
-        # self.icon = UserRoleInWorkspace.ICON[role_id]
203
+        # self.fa_icon = UserRoleInWorkspace.ICON[role_id]
204 204
         # self.role_label = UserRoleInWorkspace.LABEL[role_id]
205 205
         # self.css_style = UserRoleInWorkspace.STYLE[role_id]
206 206
 
@@ -265,11 +265,12 @@ class ActionDescription(object):
265 265
     def __init__(self, id):
266 266
         assert id in ActionDescription.allowed_values()
267 267
         self.id = id
268
-        # FIXME - G.M - 17-04-2018 - Label and icon needed for webdav
268
+        # FIXME - G.M - 17-04-2018 - Label and fa_icon needed for webdav
269 269
         #  design template,
270 270
         # find a way to not rely on this.
271 271
         self.label = self.id
272
-        self.icon = ActionDescription._ICONS[id]
272
+        self.fa_icon = ActionDescription._ICONS[id]
273
+        #self.icon = self.fa_icon
273 274
         # TODO - G.M - 10-04-2018 - [Cleanup] Drop this
274 275
         # self.label = ActionDescription._LABELS[id]
275 276
 
@@ -343,7 +344,7 @@ class ContentStatus(object):
343 344
         self.id = id
344 345
         self.label = self.id
345 346
         # TODO - G.M - 10-04-2018 - [Cleanup] Drop this
346
-        # self.icon = ContentStatus._ICONS[id]
347
+        # self.fa_icon = ContentStatus._ICONS[id]
347 348
         # self.css = ContentStatus._CSS[id]
348 349
         #
349 350
         # if type==ContentType.Thread:
@@ -520,7 +521,7 @@ class ContentType(object):
520 521
     def __init__(self, type):
521 522
         self.id = type
522 523
         # TODO - G.M - 10-04-2018 - [Cleanup] Drop this
523
-        # self.icon = ContentType._CSS_ICONS[type]
524
+        # self.fa_icon = ContentType._CSS_ICONS[type]
524 525
         # self.color = ContentType._CSS_COLORS[type]  # deprecated
525 526
         # self.css = ContentType._CSS_COLORS[type]
526 527
         # self.label = ContentType._LABEL[type]
@@ -530,7 +531,7 @@ class ContentType(object):
530 531
         return dict(id=self.type,
531 532
                     type=self.type,
532 533
                     # TODO - G.M - 10-04-2018 - [Cleanup] Drop this
533
-                    # icon=self.icon,
534
+                    # fa_icon=self.fa_icon,
534 535
                     # color=self.color,
535 536
                     # label=self.label,
536 537
                     priority=self.priority)
@@ -1457,7 +1458,7 @@ class VirtualEvent(object):
1457 1458
         assert hasattr(type, 'id')
1458 1459
         # TODO - G.M - 10-04-2018 - [Cleanup] Drop this
1459 1460
         # assert hasattr(type, 'css')
1460
-        # assert hasattr(type, 'icon')
1461
+        # assert hasattr(type, 'fa_icon')
1461 1462
         # assert hasattr(type, 'label')
1462 1463
 
1463 1464
     def created_as_delta(self, delta_from_datetime:datetime=None):

+ 5 - 5
tracim/models/workspace_menu_entries.py View File

@@ -14,7 +14,7 @@ class WorkspaceMenuEntry(object):
14 14
             self,
15 15
             label: str,
16 16
             slug: str,
17
-            icon: str,
17
+            fa_icon: str,
18 18
             hexcolor: str,
19 19
             route: str,
20 20
     ) -> None:
@@ -22,21 +22,21 @@ class WorkspaceMenuEntry(object):
22 22
         self.label = label
23 23
         self.route = route
24 24
         self.hexcolor = hexcolor
25
-        self.icon = icon
25
+        self.fa_icon = fa_icon
26 26
 
27 27
 dashboard_menu_entry = WorkspaceMenuEntry(
28 28
   slug='dashboard',
29 29
   label='Dashboard',
30 30
   route='/#/workspaces/{workspace_id}/dashboard',
31 31
   hexcolor='#252525',
32
-  icon="",
32
+  fa_icon="",
33 33
 )
34 34
 all_content_menu_entry = WorkspaceMenuEntry(
35 35
   slug="contents/all",
36 36
   label="All Contents",
37 37
   route="/#/workspaces/{workspace_id}/contents",
38 38
   hexcolor="#fdfdfd",
39
-  icon="",
39
+  fa_icon="",
40 40
 )
41 41
 
42 42
 # TODO - G.M - 08-06-2018 - This is hardcoded default menu entry,
@@ -57,7 +57,7 @@ def default_workspace_menu_entry(
57 57
                 slug=app.slug,
58 58
                 label=app.label,
59 59
                 hexcolor=app.hexcolor,
60
-                icon=app.icon,
60
+                fa_icon=app.fa_icon,
61 61
                 route=app.main_route
62 62
             )
63 63
             menu_entries.append(new_entry)

+ 5 - 6
tracim/templates/mail/content_update_body_html.mak View File

@@ -38,14 +38,14 @@
38 38
     <table style="width: 100%; cell-padding: 0; border-collapse: collapse; margin: 0">
39 39
       <tr style="background-color: F5F5F5; border-bottom: 1px solid #CCC;" >
40 40
         <td style="background-color: #666;">
41
-            <!-- FIXME - G.M - 09-06-2018 - restore logo -->
41
+            <img alt="logo" src="${logo_url}" style="vertical-align: middle;">
42 42
         </td>
43 43
         <td style="padding: 0.5em; background-color: #666; text-align: left;">
44 44
           <span style="font-size: 1.3em; color: #FFF; font-weight: bold;">
45 45
             ${main_title}
46 46
             &mdash;&nbsp;<span style="font-weight: bold; color: #999; font-weight: bold;">
47
-              ${status|n}
48
-              <!-- FIXME - G.M - 09-06-2018 - restore icon -->
47
+              ${status_label|n}
48
+              <img alt="status_icon" src="${status_icon_url}" style="vertical-align: middle;">
49 49
             </span>
50 50
         </td>
51 51
       </tr>
@@ -54,15 +54,14 @@
54 54
     <p id="content-intro">${content_intro|n}</p>
55 55
     <div id="content-body">
56 56
         <div>${content_text|n}</div>
57
-        <!-- FIXME - G.M - 09-06-2018 - fix action url -->
58 57
         <div href='' id="call-to-action-container">
59 58
         </div>
60 59
     </div>
61 60
     
62 61
     <div id="footer">
63 62
         <p>
64
-            <!-- FIXME - G.M - 09-06-2018 - Set correct workspace url -->
65
-            ${_('{user_display_name}, you receive this email because you are registered on <i>{website_title}</i> and you are <i>{user_role_label}</i> in the workspace <a href="{workspace_url}">{workspace_label}</a>.').format(user_display_name=user.display_name, user_role_label=role, workspace_url='', workspace_label=workspace.label, website_title=config.WEBSITE_TITLE)|n}
63
+
64
+            ${_('{user_display_name}, you receive this email because you are registered on <i>{website_title}</i> and you are <i>{user_role_label}</i> in the workspace <a href="{workspace_url}">{workspace_label}</a>.').format(user_display_name=user.display_name, user_role_label=role_label, workspace_url=workspace_url, workspace_label=workspace.label, website_title=config.WEBSITE_TITLE)|n}
66 65
         </p>
67 66
         <hr/>
68 67
         <p>

+ 1 - 1
tracim/templates/mail/content_update_body_text.mak View File

@@ -16,7 +16,7 @@ Hope you'll switch your mail client configuration and enjoy Tracim :)
16 16
 
17 17
 
18 18
 You receive this email because you are registered on /${config.WEBSITE_TITLE}/
19
-and you are /${role}/ in the workspace /${workspace.label}/
19
+and you are /${role_label}/ in the workspace /${workspace.label}/
20 20
 
21 21
 ----
22 22
 

+ 1 - 1
tracim/templates/mail/created_account_body_html.mak View File

@@ -38,7 +38,7 @@
38 38
     <table style="width: 100%; cell-padding: 0; border-collapse: collapse; margin: 0">
39 39
       <tr style="background-color: F5F5F5; border-bottom: 1px solid #CCC;" >
40 40
         <td style="background-color: #666;">
41
-            <!-- FIXME - G.M - 09-06-2018 - restore logo -->
41
+            <img alt="logo" src="${logo_url}" style="vertical-align: middle;">
42 42
         </td>
43 43
         <td style="padding: 0.5em; background-color: #666; text-align: left;">
44 44
           <span style="font-size: 1.3em; color: #FFF; font-weight: bold;">

+ 5 - 5
tracim/tests/functional/test_system.py View File

@@ -26,35 +26,35 @@ class TestApplicationsEndpoint(FunctionalTest):
26 26
         application = res[0]
27 27
         assert application['label'] == "Text Documents"
28 28
         assert application['slug'] == 'contents/htmlpage'
29
-        assert application['icon'] == 'file-text-o'
29
+        assert application['fa_icon'] == 'file-text-o'
30 30
         assert application['hexcolor'] == '#3f52e3'
31 31
         assert application['is_active'] is True
32 32
         assert 'config' in application
33 33
         application = res[1]
34 34
         assert application['label'] == "Markdown Plus Documents"
35 35
         assert application['slug'] == 'contents/markdownpluspage'
36
-        assert application['icon'] == 'file-code'
36
+        assert application['fa_icon'] == 'file-code'
37 37
         assert application['hexcolor'] == '#f12d2d'
38 38
         assert application['is_active'] is True
39 39
         assert 'config' in application
40 40
         application = res[2]
41 41
         assert application['label'] == "Files"
42 42
         assert application['slug'] == 'contents/files'
43
-        assert application['icon'] == 'paperclip'
43
+        assert application['fa_icon'] == 'paperclip'
44 44
         assert application['hexcolor'] == '#FF9900'
45 45
         assert application['is_active'] is True
46 46
         assert 'config' in application
47 47
         application = res[3]
48 48
         assert application['label'] == "Threads"
49 49
         assert application['slug'] == 'contents/threads'
50
-        assert application['icon'] == 'comments-o'
50
+        assert application['fa_icon'] == 'comments-o'
51 51
         assert application['hexcolor'] == '#ad4cf9'
52 52
         assert application['is_active'] is True
53 53
         assert 'config' in application
54 54
         application = res[4]
55 55
         assert application['label'] == "Calendar"
56 56
         assert application['slug'] == 'calendar'
57
-        assert application['icon'] == 'calendar-alt'
57
+        assert application['fa_icon'] == 'calendar-alt'
58 58
         assert application['hexcolor'] == '#757575'
59 59
         assert application['is_active'] is True
60 60
         assert 'config' in application

+ 7 - 7
tracim/tests/functional/test_user.py View File

@@ -37,49 +37,49 @@ class TestUserWorkspaceEndpoint(FunctionalTest):
37 37
         assert sidebar_entry['label'] == 'Dashboard'
38 38
         assert sidebar_entry['route'] == '/#/workspaces/1/dashboard'  # nopep8
39 39
         assert sidebar_entry['hexcolor'] == "#252525"
40
-        assert sidebar_entry['icon'] == ""
40
+        assert sidebar_entry['fa_icon'] == ""
41 41
 
42 42
         sidebar_entry = workspace['sidebar_entries'][1]
43 43
         assert sidebar_entry['slug'] == 'contents/all'
44 44
         assert sidebar_entry['label'] == 'All Contents'
45 45
         assert sidebar_entry['route'] == "/#/workspaces/1/contents"  # nopep8
46 46
         assert sidebar_entry['hexcolor'] == "#fdfdfd"
47
-        assert sidebar_entry['icon'] == ""
47
+        assert sidebar_entry['fa_icon'] == ""
48 48
 
49 49
         sidebar_entry = workspace['sidebar_entries'][2]
50 50
         assert sidebar_entry['slug'] == 'contents/htmlpage'
51 51
         assert sidebar_entry['label'] == 'Text Documents'
52 52
         assert sidebar_entry['route'] == '/#/workspaces/1/contents?type=htmlpage'  # nopep8
53 53
         assert sidebar_entry['hexcolor'] == "#3f52e3"
54
-        assert sidebar_entry['icon'] == "file-text-o"
54
+        assert sidebar_entry['fa_icon'] == "file-text-o"
55 55
 
56 56
         sidebar_entry = workspace['sidebar_entries'][3]
57 57
         assert sidebar_entry['slug'] == 'contents/markdownpluspage'
58 58
         assert sidebar_entry['label'] == 'Markdown Plus Documents'
59 59
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=markdownpluspage"    # nopep8
60 60
         assert sidebar_entry['hexcolor'] == "#f12d2d"
61
-        assert sidebar_entry['icon'] == "file-code"
61
+        assert sidebar_entry['fa_icon'] == "file-code"
62 62
 
63 63
         sidebar_entry = workspace['sidebar_entries'][4]
64 64
         assert sidebar_entry['slug'] == 'contents/files'
65 65
         assert sidebar_entry['label'] == 'Files'
66 66
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=file"  # nopep8
67 67
         assert sidebar_entry['hexcolor'] == "#FF9900"
68
-        assert sidebar_entry['icon'] == "paperclip"
68
+        assert sidebar_entry['fa_icon'] == "paperclip"
69 69
 
70 70
         sidebar_entry = workspace['sidebar_entries'][5]
71 71
         assert sidebar_entry['slug'] == 'contents/threads'
72 72
         assert sidebar_entry['label'] == 'Threads'
73 73
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=thread"  # nopep8
74 74
         assert sidebar_entry['hexcolor'] == "#ad4cf9"
75
-        assert sidebar_entry['icon'] == "comments-o"
75
+        assert sidebar_entry['fa_icon'] == "comments-o"
76 76
 
77 77
         sidebar_entry = workspace['sidebar_entries'][6]
78 78
         assert sidebar_entry['slug'] == 'calendar'
79 79
         assert sidebar_entry['label'] == 'Calendar'
80 80
         assert sidebar_entry['route'] == "/#/workspaces/1/calendar"  # nopep8
81 81
         assert sidebar_entry['hexcolor'] == "#757575"
82
-        assert sidebar_entry['icon'] == "calendar-alt"
82
+        assert sidebar_entry['fa_icon'] == "calendar-alt"
83 83
 
84 84
     def test_api__get_user_workspaces__err_403__unallowed_user(self):
85 85
         """

+ 7 - 7
tracim/tests/functional/test_workspaces.py View File

@@ -38,49 +38,49 @@ class TestWorkspaceEndpoint(FunctionalTest):
38 38
         assert sidebar_entry['label'] == 'Dashboard'
39 39
         assert sidebar_entry['route'] == '/#/workspaces/1/dashboard'  # nopep8
40 40
         assert sidebar_entry['hexcolor'] == "#252525"
41
-        assert sidebar_entry['icon'] == ""
41
+        assert sidebar_entry['fa_icon'] == ""
42 42
 
43 43
         sidebar_entry = workspace['sidebar_entries'][1]
44 44
         assert sidebar_entry['slug'] == 'contents/all'
45 45
         assert sidebar_entry['label'] == 'All Contents'
46 46
         assert sidebar_entry['route'] == "/#/workspaces/1/contents"  # nopep8
47 47
         assert sidebar_entry['hexcolor'] == "#fdfdfd"
48
-        assert sidebar_entry['icon'] == ""
48
+        assert sidebar_entry['fa_icon'] == ""
49 49
 
50 50
         sidebar_entry = workspace['sidebar_entries'][2]
51 51
         assert sidebar_entry['slug'] == 'contents/htmlpage'
52 52
         assert sidebar_entry['label'] == 'Text Documents'
53 53
         assert sidebar_entry['route'] == '/#/workspaces/1/contents?type=htmlpage'  # nopep8
54 54
         assert sidebar_entry['hexcolor'] == "#3f52e3"
55
-        assert sidebar_entry['icon'] == "file-text-o"
55
+        assert sidebar_entry['fa_icon'] == "file-text-o"
56 56
 
57 57
         sidebar_entry = workspace['sidebar_entries'][3]
58 58
         assert sidebar_entry['slug'] == 'contents/markdownpluspage'
59 59
         assert sidebar_entry['label'] == 'Markdown Plus Documents'
60 60
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=markdownpluspage"    # nopep8
61 61
         assert sidebar_entry['hexcolor'] == "#f12d2d"
62
-        assert sidebar_entry['icon'] == "file-code"
62
+        assert sidebar_entry['fa_icon'] == "file-code"
63 63
 
64 64
         sidebar_entry = workspace['sidebar_entries'][4]
65 65
         assert sidebar_entry['slug'] == 'contents/files'
66 66
         assert sidebar_entry['label'] == 'Files'
67 67
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=file"  # nopep8
68 68
         assert sidebar_entry['hexcolor'] == "#FF9900"
69
-        assert sidebar_entry['icon'] == "paperclip"
69
+        assert sidebar_entry['fa_icon'] == "paperclip"
70 70
 
71 71
         sidebar_entry = workspace['sidebar_entries'][5]
72 72
         assert sidebar_entry['slug'] == 'contents/threads'
73 73
         assert sidebar_entry['label'] == 'Threads'
74 74
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=thread"  # nopep8
75 75
         assert sidebar_entry['hexcolor'] == "#ad4cf9"
76
-        assert sidebar_entry['icon'] == "comments-o"
76
+        assert sidebar_entry['fa_icon'] == "comments-o"
77 77
 
78 78
         sidebar_entry = workspace['sidebar_entries'][6]
79 79
         assert sidebar_entry['slug'] == 'calendar'
80 80
         assert sidebar_entry['label'] == 'Calendar'
81 81
         assert sidebar_entry['route'] == "/#/workspaces/1/calendar"  # nopep8
82 82
         assert sidebar_entry['hexcolor'] == "#757575"
83
-        assert sidebar_entry['icon'] == "calendar-alt"
83
+        assert sidebar_entry['fa_icon'] == "calendar-alt"
84 84
 
85 85
     def test_api__get_workspace__err_403__unallowed_user(self) -> None:
86 86
         """

+ 2 - 2
tracim/views/core_api/schemas.py View File

@@ -114,7 +114,7 @@ class WorkspaceMenuEntrySchema(marshmallow.Schema):
114 114
                     'which must be replaced on backend size '
115 115
                     '(the route must be ready-to-use)'
116 116
     )
117
-    icon = marshmallow.fields.String(
117
+    fa_icon = marshmallow.fields.String(
118 118
         example='file-text-o',
119 119
         description='CSS class of the icon. Example: file-o for using Fontawesome file-text-o icon',  # nopep8
120 120
     )
@@ -170,7 +170,7 @@ class ApplicationConfigSchema(marshmallow.Schema):
170 170
 class ApplicationSchema(marshmallow.Schema):
171 171
     label = marshmallow.fields.String(example='Calendar')
172 172
     slug = marshmallow.fields.String(example='calendar')
173
-    icon = marshmallow.fields.String(
173
+    fa_icon = marshmallow.fields.String(
174 174
         example='file-o',
175 175
         description='CSS class of the icon. Example: file-o for using Fontawesome file-o icon',  # nopep8
176 176
     )

+ 2 - 2
tracim/views/core_api/session_controller.py View File

@@ -13,7 +13,7 @@ from tracim.views.core_api.schemas import UserSchema
13 13
 from tracim.views.core_api.schemas import NoContentSchema
14 14
 from tracim.views.core_api.schemas import LoginOutputHeaders
15 15
 from tracim.views.core_api.schemas import BasicAuthSchema
16
-from tracim.exceptions import NotAuthentificated
16
+from tracim.exceptions import NotAuthenticated
17 17
 from tracim.exceptions import AuthenticationFailed
18 18
 
19 19
 
@@ -52,7 +52,7 @@ class SessionController(Controller):
52 52
         return
53 53
 
54 54
     @hapic.with_api_doc()
55
-    @hapic.handle_exception(NotAuthentificated, HTTPStatus.UNAUTHORIZED)
55
+    @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
56 56
     @hapic.output_body(UserSchema(),)
57 57
     def whoami(self, context, request: TracimRequest, hapic_data=None):
58 58
         """

+ 2 - 2
tracim/views/core_api/system_controller.py View File

@@ -1,7 +1,7 @@
1 1
 # coding=utf-8
2 2
 from pyramid.config import Configurator
3 3
 
4
-from tracim.exceptions import NotAuthentificated, InsufficientUserProfile
4
+from tracim.exceptions import NotAuthenticated, InsufficientUserProfile
5 5
 from tracim.lib.utils.authorization import require_profile
6 6
 from tracim.models import Group
7 7
 from tracim.models.applications import applications
@@ -20,7 +20,7 @@ from tracim.views.core_api.schemas import ApplicationSchema
20 20
 class SystemController(Controller):
21 21
 
22 22
     @hapic.with_api_doc()
23
-    @hapic.handle_exception(NotAuthentificated, HTTPStatus.UNAUTHORIZED)
23
+    @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
24 24
     @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
25 25
     @require_profile(Group.TIM_USER)
26 26
     @hapic.output_body(ApplicationSchema(many=True),)

+ 2 - 2
tracim/views/core_api/user_controller.py View File

@@ -12,7 +12,7 @@ except ImportError:
12 12
 
13 13
 from tracim import hapic, TracimRequest
14 14
 
15
-from tracim.exceptions import NotAuthentificated
15
+from tracim.exceptions import NotAuthenticated
16 16
 from tracim.exceptions import InsufficientUserProfile
17 17
 from tracim.exceptions import UserDoesNotExist
18 18
 from tracim.lib.core.workspace import WorkspaceApi
@@ -24,7 +24,7 @@ from tracim.views.core_api.schemas import WorkspaceDigestSchema
24 24
 class UserController(Controller):
25 25
 
26 26
     @hapic.with_api_doc()
27
-    @hapic.handle_exception(NotAuthentificated, HTTPStatus.UNAUTHORIZED)
27
+    @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
28 28
     @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
29 29
     @hapic.handle_exception(UserDoesNotExist, HTTPStatus.NOT_FOUND)
30 30
     @require_same_user_or_profile(Group.TIM_ADMIN)

+ 3 - 3
tracim/views/core_api/workspace_controller.py View File

@@ -15,7 +15,7 @@ except ImportError:
15 15
     from http import client as HTTPStatus
16 16
 
17 17
 from tracim import hapic, TracimRequest
18
-from tracim.exceptions import NotAuthentificated
18
+from tracim.exceptions import NotAuthenticated
19 19
 from tracim.exceptions import InsufficientUserProfile
20 20
 from tracim.exceptions import WorkspaceNotFound
21 21
 from tracim.lib.core.user import UserApi
@@ -29,7 +29,7 @@ from tracim.views.core_api.schemas import WorkspaceMemberSchema
29 29
 class WorkspaceController(Controller):
30 30
 
31 31
     @hapic.with_api_doc()
32
-    @hapic.handle_exception(NotAuthentificated, HTTPStatus.UNAUTHORIZED)
32
+    @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
33 33
     @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
34 34
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
35 35
     @require_workspace_role(UserRoleInWorkspace.READER)
@@ -49,7 +49,7 @@ class WorkspaceController(Controller):
49 49
         return wapi.get_workspace_with_context(request.current_workspace)
50 50
 
51 51
     @hapic.with_api_doc()
52
-    @hapic.handle_exception(NotAuthentificated, HTTPStatus.UNAUTHORIZED)
52
+    @hapic.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED)
53 53
     @hapic.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN)
54 54
     @hapic.handle_exception(WorkspaceNotFound, HTTPStatus.FORBIDDEN)
55 55
     @require_workspace_role(UserRoleInWorkspace.READER)