Browse Source

Merge branch 'develop' of github.com:tracim/tracim_v2 into feature/automated_setup_and_better_uwsgi_doc

Guénaël Muller 6 years ago
parent
commit
e6ea68699a
100 changed files with 2098 additions and 948 deletions
  1. 4 0
      .gitignore
  2. 0 1
      .travis.yml
  3. 7 0
      backend/development.ini.sample
  4. 12 0
      backend/doc/roles.md
  5. 3 0
      backend/setup.py
  6. 74 1
      backend/tests_configs.ini
  7. 9 4
      backend/tracim_backend/__init__.py
  8. 41 7
      backend/tracim_backend/config.py
  9. 8 0
      backend/tracim_backend/exceptions.py
  10. 18 18
      backend/tracim_backend/fixtures/content.py
  11. 65 59
      backend/tracim_backend/lib/core/content.py
  12. 1 1
      backend/tracim_backend/lib/core/notifications.py
  13. 41 3
      backend/tracim_backend/lib/core/user.py
  14. 1 1
      backend/tracim_backend/lib/core/userworkspace.py
  15. 1 1
      backend/tracim_backend/lib/core/workspace.py
  16. 46 26
      backend/tracim_backend/lib/mail_notifier/notifier.py
  17. 6 6
      backend/tracim_backend/lib/utils/authorization.py
  18. 7 3
      backend/tracim_backend/lib/utils/request.py
  19. 53 0
      backend/tracim_backend/lib/utils/utils.py
  20. 7 5
      backend/tracim_backend/lib/webdav/dav_provider.py
  21. 3 2
      backend/tracim_backend/lib/webdav/design.py
  22. 37 34
      backend/tracim_backend/lib/webdav/resources.py
  23. 5 3
      backend/tracim_backend/lib/webdav/utils.py
  24. 20 2
      backend/tracim_backend/models/applications.py
  25. 113 146
      backend/tracim_backend/models/contents.py
  26. 34 9
      backend/tracim_backend/models/context_models.py
  27. 26 28
      backend/tracim_backend/models/data.py
  28. 3 1
      backend/tracim_backend/templates/mail/content_update_body_html.mak
  29. 1 2
      backend/tracim_backend/templates/mail/created_account_body_html.mak
  30. 11 14
      backend/tracim_backend/tests/__init__.py
  31. 85 38
      backend/tracim_backend/tests/functional/test_contents.py
  32. 5 5
      backend/tracim_backend/tests/functional/test_mail_notification.py
  33. 25 72
      backend/tracim_backend/tests/functional/test_system.py
  34. 525 127
      backend/tracim_backend/tests/functional/test_user.py
  35. 112 41
      backend/tracim_backend/tests/functional/test_workspaces.py
  36. 115 115
      backend/tracim_backend/tests/library/test_content_api.py
  37. 183 0
      backend/tracim_backend/tests/library/test_user_api.py
  38. 1 0
      backend/tracim_backend/tests/library/test_webdav.py
  39. 14 0
      backend/tracim_backend/tests/library/tests_utils.py
  40. 6 6
      backend/tracim_backend/tests/models/test_content.py
  41. 4 3
      backend/tracim_backend/tests/models/test_content_revision.py
  42. 11 5
      backend/tracim_backend/views/contents_api/comment_controller.py
  43. 42 14
      backend/tracim_backend/views/contents_api/file_controller.py
  44. 13 5
      backend/tracim_backend/views/contents_api/html_document_controller.py
  45. 13 5
      backend/tracim_backend/views/contents_api/threads_controller.py
  46. 26 12
      backend/tracim_backend/views/core_api/schemas.py
  47. 3 3
      backend/tracim_backend/views/core_api/system_controller.py
  48. 59 2
      backend/tracim_backend/views/core_api/user_controller.py
  49. 28 13
      backend/tracim_backend/views/core_api/workspace_controller.py
  50. 67 0
      backend/tracim_backend/views/frontend.py
  51. 1 1
      bash_library.sh
  52. 40 51
      build_full_frontend.sh
  53. 6 0
      color.json.sample
  54. 3 1
      frontend/.gitignore
  55. 0 0
      frontend/dist/asset/bootstrap/bootstrap-4.0.0-beta.2.js
  56. 0 0
      frontend/dist/asset/bootstrap/bootstrap-4.0.0-beta.css
  57. 0 0
      frontend/dist/asset/bootstrap/jquery-3.2.1.js
  58. 0 0
      frontend/dist/asset/bootstrap/popper-1.12.3.js
  59. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/HELP-US-OUT.txt
  60. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/css/font-awesome.css
  61. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/css/font-awesome.min.css
  62. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/fonts/FontAwesome.otf
  63. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/fonts/fontawesome-webfont.eot
  64. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/fonts/fontawesome-webfont.svg
  65. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf
  66. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/fonts/fontawesome-webfont.woff
  67. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2
  68. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/animated.less
  69. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/bordered-pulled.less
  70. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/core.less
  71. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/fixed-width.less
  72. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/font-awesome.less
  73. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/icons.less
  74. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/larger.less
  75. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/list.less
  76. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/mixins.less
  77. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/path.less
  78. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/rotated-flipped.less
  79. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/screen-reader.less
  80. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/stacked.less
  81. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/less/variables.less
  82. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_animated.scss
  83. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_bordered-pulled.scss
  84. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_core.scss
  85. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_fixed-width.scss
  86. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_icons.scss
  87. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_larger.scss
  88. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_list.scss
  89. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_mixins.scss
  90. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_path.scss
  91. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_rotated-flipped.scss
  92. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_screen-reader.scss
  93. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_stacked.scss
  94. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/_variables.scss
  95. 0 0
      frontend/dist/asset/font/font-awesome-4.7.0/scss/font-awesome.scss
  96. 3 0
      frontend/dist/asset/tracim/appInterface.js
  97. 1 0
      frontend/dist/asset/tracim/tinymceInit.js
  98. BIN
      frontend/dist/ecbb61e619a4d2801db1054c019316cc.jpg
  99. 50 52
      frontend/dist/index.html
  100. 0 0
      frontend/dist/index.mak

+ 4 - 0
.gitignore View File

1
 .idea/
1
 .idea/
2
 frontend/dist/app/
2
 frontend/dist/app/
3
+frontend/dist/tracim.app.entry.js
4
+frontend/dist/asset/tracim/tracim.vendor.bundle.js
3
 frontend_app_html-document/dist/html-document.app.js
5
 frontend_app_html-document/dist/html-document.app.js
4
 frontend_lib/dist/tracim_frontend_lib.js
6
 frontend_lib/dist/tracim_frontend_lib.js
7
+npm-debug.log
8
+package-lock.json

+ 0 - 1
.travis.yml View File

3
     - sudo: false
3
     - sudo: false
4
       language: python
4
       language: python
5
       python:
5
       python:
6
-        - "3.4"
7
         - "3.5"
6
         - "3.5"
8
         - "3.6"
7
         - "3.6"
9
 
8
 

+ 7 - 0
backend/development.ini.sample View File

186
 ## return error
186
 ## return error
187
 # preview.jpg.restricted_dims = True
187
 # preview.jpg.restricted_dims = True
188
 
188
 
189
+### Frontend
190
+frontend.serve = True
191
+# You can set dist folder of tracim frontend. by default, system
192
+# will try to get it automatically according to tracim_v2 repository
193
+# organisation.
194
+# frontend.dist_folder_path = /home/user/tracim_v2/frontend/dist
195
+
189
 ###
196
 ###
190
 # wsgi server configuration
197
 # wsgi server configuration
191
 ###
198
 ###

+ 12 - 0
backend/doc/roles.md View File

7
 
7
 
8
 ## Global profile
8
 ## Global profile
9
 
9
 
10
+|                               | Normal User | Managers    | Admin          |
11
+|-------------------------------|-------------|-------------|----------------|
12
+| slug                            | users       | managers    | administrators |
13
+|-------------------------------|-------------|-------------|---------|
14
+
10
 
15
 
11
 |                               | Normal User | Managers    | Admin   |
16
 |                               | Normal User | Managers    | Admin   |
12
 |-------------------------------|-------------|-------------|---------|
17
 |-------------------------------|-------------|-------------|---------|
22
 | access to all user data (/users/{user_id} endpoints) |personal-only|personal-only| yes     |
27
 | access to all user data (/users/{user_id} endpoints) |personal-only|personal-only| yes     |
23
 
28
 
24
 
29
 
30
+
31
+
25
 ## Workspace Roles
32
 ## Workspace Roles
26
 
33
 
27
 
34
 
28
 |                              | Reader | Contributor | Content Manager | Workspace Manager |
35
 |                              | Reader | Contributor | Content Manager | Workspace Manager |
29
 |------------------------------|--------|-------------|-----------------|-------------------|
36
 |------------------------------|--------|-------------|-----------------|-------------------|
37
+| slug                         | reader | contributor | content-manager |  workspace-manager|
38
+|------------------------------|--------|-------------|-----------------|-------------------|
39
+
40
+|                              | Reader | Contributor | Content Manager | Workspace Manager |
41
+|------------------------------|--------|-------------|-----------------|-------------------|
30
 | read content                 |  yes   | yes         | yes             | yes               |
42
 | read content                 |  yes   | yes         | yes             | yes               |
31
 |------------------------------|--------|-------------|-----------------|-------------------|
43
 |------------------------------|--------|-------------|-----------------|-------------------|
32
 | create content               |  no    | yes         | yes             | yes               |
44
 | create content               |  no    | yes         | yes             | yes               |

+ 3 - 0
backend/setup.py View File

41
     'lxml',
41
     'lxml',
42
     'redis',
42
     'redis',
43
     'rq',
43
     'rq',
44
+    # frontend file serve
45
+    'pyramid_mako',
46
+    'spectra',
44
 ]
47
 ]
45
 
48
 
46
 tests_require = [
49
 tests_require = [

+ 74 - 1
backend/tests_configs.ini View File

4
 depot_storage_dir = /tmp/test/depot
4
 depot_storage_dir = /tmp/test/depot
5
 user.auth_token.validity = 604800
5
 user.auth_token.validity = 604800
6
 preview_cache_dir = /tmp/test/preview_cache
6
 preview_cache_dir = /tmp/test/preview_cache
7
-
7
+website.base_url = http://localhost:6543
8
 [app:command_test]
8
 [app:command_test]
9
 use = egg:tracim_backend
9
 use = egg:tracim_backend
10
 sqlalchemy.url = sqlite:///tracim_test.sqlite
10
 sqlalchemy.url = sqlite:///tracim_test.sqlite
12
 depot_storage_dir = /tmp/test/depot
12
 depot_storage_dir = /tmp/test/depot
13
 user.auth_token.validity = 604800
13
 user.auth_token.validity = 604800
14
 preview_cache_dir = /tmp/test/preview_cache
14
 preview_cache_dir = /tmp/test/preview_cache
15
+website.base_url = http://localhost:6543
15
 
16
 
16
 [mail_test]
17
 [mail_test]
17
 sqlalchemy.url = sqlite:///:memory:
18
 sqlalchemy.url = sqlite:///:memory:
37
 email.notification.smtp.port = 1025
38
 email.notification.smtp.port = 1025
38
 email.notification.smtp.user = test_user
39
 email.notification.smtp.user = test_user
39
 email.notification.smtp.password = just_a_password
40
 email.notification.smtp.password = just_a_password
41
+website.base_url = http://localhost:6543
40
 
42
 
41
 [mail_test_async]
43
 [mail_test_async]
42
 sqlalchemy.url = sqlite:///:memory:
44
 sqlalchemy.url = sqlite:///:memory:
63
 email.notification.smtp.port = 1025
65
 email.notification.smtp.port = 1025
64
 email.notification.smtp.user = test_user
66
 email.notification.smtp.user = test_user
65
 email.notification.smtp.password = just_a_password
67
 email.notification.smtp.password = just_a_password
68
+website.base_url = http://localhost:6543
69
+
70
+[functional_test]
71
+sqlalchemy.url = sqlite:///tracim_test.sqlite
72
+depot_storage_name = test
73
+depot_storage_dir = /tmp/test/depot
74
+user.auth_token.validity = 604800
75
+preview_cache_dir = /tmp/test/preview_cache
76
+preview.jpg.restricted_dims = True
77
+email.notification.activated = false
78
+website.base_url = http://localhost:6543
79
+
80
+[functional_test_no_db]
81
+sqlalchemy.url = sqlite://
82
+depot_storage_name = test
83
+depot_storage_dir = /tmp/test/depot
84
+user.auth_token.validity = 604800
85
+preview_cache_dir = /tmp/test/preview_cache
86
+preview.jpg.restricted_dims = True
87
+email.notification.activated = false
88
+website.base_url = http://localhost:6543
89
+
90
+[functional_test_with_mail_test_sync]
91
+sqlalchemy.url = sqlite:///tracim_test.sqlite
92
+depot_storage_name = test
93
+depot_storage_dir = /tmp/test/depot
94
+user.auth_token.validity = 604800
95
+preview_cache_dir = /tmp/test/preview_cache
96
+preview.jpg.restricted_dims = True
97
+email.notification.activated = true
98
+email.notification.from.email = test_user_from+{user_id}@localhost
99
+email.notification.from.default_label = Tracim Notifications
100
+email.notification.reply_to.email = test_user_reply+{content_id}@localhost
101
+email.notification.references.email = test_user_refs+{content_id}@localhost
102
+email.notification.content_update.template.html = %(here)s/tracim_backend/templates/mail/content_update_body_html.mak
103
+email.notification.content_update.template.text = %(here)s/tracim_backend/templates/mail/content_update_body_text.mak
104
+email.notification.created_account.template.html = %(here)s/tracim_backend/templates/mail/created_account_body_html.mak
105
+email.notification.created_account.template.text = %(here)s/tracim_backend/templates/mail/created_account_body_text.mak
106
+email.notification.content_update.subject = [{website_title}] [{workspace_label}] {content_label} ({content_status_label})
107
+email.notification.created_account.subject = [{website_title}] Created account
108
+email.notification.processing_mode = sync
109
+email.notification.smtp.server = 127.0.0.1
110
+email.notification.smtp.port = 1025
111
+email.notification.smtp.user = test_user
112
+email.notification.smtp.password = just_a_password
113
+website.base_url = http://localhost:6543
114
+
115
+[functional_test_with_mail_test_async]
116
+sqlalchemy.url = sqlite:///tracim_test.sqlite
117
+depot_storage_name = test
118
+depot_storage_dir = /tmp/test/depot
119
+user.auth_token.validity = 604800
120
+preview_cache_dir = /tmp/test/preview_cache
121
+preview.jpg.restricted_dims = True
122
+email.notification.activated = true
123
+email.notification.from.email = test_user_from+{user_id}@localhost
124
+email.notification.from.default_label = Tracim Notifications
125
+email.notification.reply_to.email = test_user_reply+{content_id}@localhost
126
+email.notification.references.email = test_user_refs+{content_id}@localhost
127
+email.notification.content_update.template.html = %(here)s/tracim_backend/templates/mail/content_update_body_html.mak
128
+email.notification.content_update.template.text = %(here)s/tracim_backend/templates/mail/content_update_body_text.mak
129
+email.notification.created_account.template.html = %(here)s/tracim_backend/templates/mail/created_account_body_html.mak
130
+email.notification.created_account.template.text = %(here)s/tracim_backend/templates/mail/created_account_body_text.mak
131
+email.notification.content_update.subject = [{website_title}] [{workspace_label}] {content_label} ({content_status_label})
132
+email.notification.created_account.subject = [{website_title}] Created account
133
+email.notification.processing_mode = async
134
+email.notification.smtp.server = 127.0.0.1
135
+email.notification.smtp.port = 1025
136
+email.notification.smtp.user = test_user
137
+email.notification.smtp.password = just_a_password
138
+website.base_url = http://localhost:6543

+ 9 - 4
backend/tracim_backend/__init__.py View File

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
-
3
-
4
 try:  # Python 3.5+
2
 try:  # Python 3.5+
5
     from http import HTTPStatus
3
     from http import HTTPStatus
6
 except ImportError:
4
 except ImportError:
9
 from pyramid.config import Configurator
7
 from pyramid.config import Configurator
10
 from pyramid.authentication import BasicAuthAuthenticationPolicy
8
 from pyramid.authentication import BasicAuthAuthenticationPolicy
11
 from hapic.ext.pyramid import PyramidContext
9
 from hapic.ext.pyramid import PyramidContext
12
-from pyramid.exceptions import NotFound
13
 from sqlalchemy.exc import OperationalError
10
 from sqlalchemy.exc import OperationalError
14
 
11
 
15
 from tracim_backend.extensions import hapic
12
 from tracim_backend.extensions import hapic
30
 from tracim_backend.views.core_api.workspace_controller import WorkspaceController
27
 from tracim_backend.views.core_api.workspace_controller import WorkspaceController
31
 from tracim_backend.views.contents_api.comment_controller import CommentController
28
 from tracim_backend.views.contents_api.comment_controller import CommentController
32
 from tracim_backend.views.contents_api.file_controller import FileController
29
 from tracim_backend.views.contents_api.file_controller import FileController
30
+from tracim_backend.views.frontend import FrontendController
33
 from tracim_backend.views.errors import ErrorSchema
31
 from tracim_backend.views.errors import ErrorSchema
34
 from tracim_backend.exceptions import NotAuthenticated
32
 from tracim_backend.exceptions import NotAuthenticated
33
+from tracim_backend.exceptions import PageNotFound
35
 from tracim_backend.exceptions import UserNotActive
34
 from tracim_backend.exceptions import UserNotActive
36
 from tracim_backend.exceptions import InvalidId
35
 from tracim_backend.exceptions import InvalidId
37
 from tracim_backend.exceptions import InsufficientUserProfile
36
 from tracim_backend.exceptions import InsufficientUserProfile
86
     hapic.set_context(context)
85
     hapic.set_context(context)
87
     # INFO - G.M - 2018-07-04 - global-context exceptions
86
     # INFO - G.M - 2018-07-04 - global-context exceptions
88
     # Not found
87
     # Not found
89
-    context.handle_exception(NotFound, HTTPStatus.NOT_FOUND)
88
+    context.handle_exception(PageNotFound, HTTPStatus.NOT_FOUND)
90
     # Bad request
89
     # Bad request
91
     context.handle_exception(WorkspaceNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST)  # nopep8
90
     context.handle_exception(WorkspaceNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST)  # nopep8
92
     context.handle_exception(UserNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST)  # nopep8
91
     context.handle_exception(UserNotFoundInTracimRequest, HTTPStatus.BAD_REQUEST)  # nopep8
106
     context.handle_exception(OperationalError, HTTPStatus.INTERNAL_SERVER_ERROR)
105
     context.handle_exception(OperationalError, HTTPStatus.INTERNAL_SERVER_ERROR)
107
     context.handle_exception(Exception, HTTPStatus.INTERNAL_SERVER_ERROR)
106
     context.handle_exception(Exception, HTTPStatus.INTERNAL_SERVER_ERROR)
108
 
107
 
108
+
109
     # Add controllers
109
     # Add controllers
110
     session_controller = SessionController()
110
     session_controller = SessionController()
111
     system_controller = SystemController()
111
     system_controller = SystemController()
124
     configurator.include(thread_controller.bind, route_prefix=BASE_API_V2)
124
     configurator.include(thread_controller.bind, route_prefix=BASE_API_V2)
125
     configurator.include(file_controller.bind, route_prefix=BASE_API_V2)
125
     configurator.include(file_controller.bind, route_prefix=BASE_API_V2)
126
 
126
 
127
+    if app_config.FRONTEND_SERVE:
128
+        configurator.include('pyramid_mako')
129
+        frontend_controller = FrontendController(app_config.FRONTEND_DIST_FOLDER_PATH)  # nopep8
130
+        configurator.include(frontend_controller.bind)
131
+
127
     hapic.add_documentation_view(
132
     hapic.add_documentation_view(
128
         '/api/v2/doc',
133
         '/api/v2/doc',
129
         'Tracim v2 API',
134
         'Tracim v2 API',

+ 41 - 7
backend/tracim_backend/config.py View File

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
 from urllib.parse import urlparse
2
 from urllib.parse import urlparse
3
+
4
+import os
3
 from paste.deploy.converters import asbool
5
 from paste.deploy.converters import asbool
4
 from tracim_backend.lib.utils.logger import logger
6
 from tracim_backend.lib.utils.logger import logger
5
 from depot.manager import DepotManager
7
 from depot.manager import DepotManager
8
+from tracim_backend.models.contents import CONTENT_TYPES
9
+from tracim_backend.models.data import ActionDescription
6
 
10
 
7
-from tracim_backend.models.data import ActionDescription, ContentType
8
 
11
 
9
 
12
 
10
 class CFG(object):
13
 class CFG(object):
78
             'website.base_url',
81
             'website.base_url',
79
             '',
82
             '',
80
         )
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
+            )
81
 
90
 
82
         # TODO - G.M - 26-03-2018 - [Cleanup] These params seems deprecated for tracimv2,  # nopep8
91
         # TODO - G.M - 26-03-2018 - [Cleanup] These params seems deprecated for tracimv2,  # nopep8
83
         # Verify this
92
         # Verify this
145
         ]
154
         ]
146
 
155
 
147
         self.EMAIL_NOTIFICATION_NOTIFIED_CONTENTS = [
156
         self.EMAIL_NOTIFICATION_NOTIFIED_CONTENTS = [
148
-            ContentType.Page,
149
-            ContentType.Thread,
150
-            ContentType.File,
151
-            ContentType.Comment,
152
-            # ContentType.Folder -- Folder is skipped
157
+            CONTENT_TYPES.Page.slug,
158
+            CONTENT_TYPES.Thread.slug,
159
+            CONTENT_TYPES.File.slug,
160
+            CONTENT_TYPES.Comment.slug,
161
+            # CONTENT_TYPES.Folder.slug -- Folder is skipped
153
         ]
162
         ]
154
         if settings.get('email.notification.from'):
163
         if settings.get('email.notification.from'):
155
             raise Exception(
164
             raise Exception(
160
 
169
 
161
         self.EMAIL_NOTIFICATION_FROM_EMAIL = settings.get(
170
         self.EMAIL_NOTIFICATION_FROM_EMAIL = settings.get(
162
             'email.notification.from.email',
171
             'email.notification.from.email',
172
+            'noreply+{user_id}@trac.im'
163
         )
173
         )
164
         self.EMAIL_NOTIFICATION_FROM_DEFAULT_LABEL = settings.get(
174
         self.EMAIL_NOTIFICATION_FROM_DEFAULT_LABEL = settings.get(
165
-            'email.notification.from.default_label'
175
+            'email.notification.from.default_label',
176
+            'Tracim Notifications'
166
         )
177
         )
167
         self.EMAIL_NOTIFICATION_REPLY_TO_EMAIL = settings.get(
178
         self.EMAIL_NOTIFICATION_REPLY_TO_EMAIL = settings.get(
168
             'email.notification.reply_to.email',
179
             'email.notification.reply_to.email',
432
 
443
 
433
         self.PREVIEW_JPG_ALLOWED_DIMS = allowed_dims
444
         self.PREVIEW_JPG_ALLOWED_DIMS = allowed_dims
434
 
445
 
446
+        self.FRONTEND_SERVE = asbool(settings.get(
447
+            'frontend.serve', False
448
+        ))
449
+
450
+        # INFO - G.M - 2018-08-06 - we pretend that frontend_dist_folder
451
+        # is probably in frontend subfolder
452
+        # of tracim_v2 parent of both backend and frontend
453
+        backend_folder = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # nopep8
454
+        tracim_v2_folder = os.path.dirname(backend_folder)
455
+        frontend_dist_folder = os.path.join(tracim_v2_folder, 'frontend', 'dist')  # nopep8
456
+
457
+        self.FRONTEND_DIST_FOLDER_PATH = settings.get(
458
+            'frontend.dist_folder_path', frontend_dist_folder
459
+        )
460
+
461
+        # INFO - G.M - 2018-08-06 - We check dist folder existence
462
+        if self.FRONTEND_SERVE and not os.path.isdir(self.FRONTEND_DIST_FOLDER_PATH):  # nopep8
463
+            raise Exception(
464
+                'ERROR: {} folder does not exist as folder. '
465
+                'please set frontend.dist_folder.path'
466
+                'with a correct value'.format(self.FRONTEND_DIST_FOLDER_PATH)
467
+            )
468
+
435
     def configure_filedepot(self):
469
     def configure_filedepot(self):
436
         depot_storage_name = self.DEPOT_STORAGE_NAME
470
         depot_storage_name = self.DEPOT_STORAGE_NAME
437
         depot_storage_path = self.DEPOT_STORAGE_DIR
471
         depot_storage_path = self.DEPOT_STORAGE_DIR

+ 8 - 0
backend/tracim_backend/exceptions.py View File

203
 
203
 
204
 class PreviewDimNotAllowed(TracimException):
204
 class PreviewDimNotAllowed(TracimException):
205
     pass
205
     pass
206
+
207
+
208
+class TooShortAutocompleteString(TracimException):
209
+    pass
210
+
211
+
212
+class PageNotFound(TracimException):
213
+    pass

+ 18 - 18
backend/tracim_backend/fixtures/content.py View File

8
 from tracim_backend.lib.core.content import ContentApi
8
 from tracim_backend.lib.core.content import ContentApi
9
 from tracim_backend.lib.core.userworkspace import RoleApi
9
 from tracim_backend.lib.core.userworkspace import RoleApi
10
 from tracim_backend.lib.core.workspace import WorkspaceApi
10
 from tracim_backend.lib.core.workspace import WorkspaceApi
11
-from tracim_backend.models.data import ContentType
11
+from tracim_backend.models.contents import CONTENT_TYPES
12
 from tracim_backend.models.data import UserRoleInWorkspace
12
 from tracim_backend.models.data import UserRoleInWorkspace
13
 from tracim_backend.models.revision_protection import new_revision
13
 from tracim_backend.models.revision_protection import new_revision
14
 
14
 
91
         # Folders
91
         # Folders
92
 
92
 
93
         tool_workspace = content_api.create(
93
         tool_workspace = content_api.create(
94
-            content_type=ContentType.Folder,
94
+            content_type_slug=CONTENT_TYPES.Folder.slug,
95
             workspace=business_workspace,
95
             workspace=business_workspace,
96
             label='Tools',
96
             label='Tools',
97
             do_save=True,
97
             do_save=True,
98
             do_notify=False,
98
             do_notify=False,
99
         )
99
         )
100
         menu_workspace = content_api.create(
100
         menu_workspace = content_api.create(
101
-            content_type=ContentType.Folder,
101
+            content_type_slug=CONTENT_TYPES.Folder.slug,
102
             workspace=business_workspace,
102
             workspace=business_workspace,
103
             label='Menus',
103
             label='Menus',
104
             do_save=True,
104
             do_save=True,
106
         )
106
         )
107
 
107
 
108
         dessert_folder = content_api.create(
108
         dessert_folder = content_api.create(
109
-            content_type=ContentType.Folder,
109
+            content_type_slug=CONTENT_TYPES.Folder.slug,
110
             workspace=recipe_workspace,
110
             workspace=recipe_workspace,
111
             label='Desserts',
111
             label='Desserts',
112
             do_save=True,
112
             do_save=True,
113
             do_notify=False,
113
             do_notify=False,
114
         )
114
         )
115
         salads_folder = content_api.create(
115
         salads_folder = content_api.create(
116
-            content_type=ContentType.Folder,
116
+            content_type_slug=CONTENT_TYPES.Folder.slug,
117
             workspace=recipe_workspace,
117
             workspace=recipe_workspace,
118
             label='Salads',
118
             label='Salads',
119
             do_save=True,
119
             do_save=True,
120
             do_notify=False,
120
             do_notify=False,
121
         )
121
         )
122
         other_folder = content_api.create(
122
         other_folder = content_api.create(
123
-            content_type=ContentType.Folder,
123
+            content_type_slug=CONTENT_TYPES.Folder.slug,
124
             workspace=other_workspace,
124
             workspace=other_workspace,
125
             label='Infos',
125
             label='Infos',
126
             do_save=True,
126
             do_save=True,
129
 
129
 
130
         # Pages, threads, ..
130
         # Pages, threads, ..
131
         tiramisu_page = content_api.create(
131
         tiramisu_page = content_api.create(
132
-            content_type=ContentType.Page,
132
+            content_type_slug=CONTENT_TYPES.Page.slug,
133
             workspace=recipe_workspace,
133
             workspace=recipe_workspace,
134
             parent=dessert_folder,
134
             parent=dessert_folder,
135
             label='Tiramisu Recipes!!!',
135
             label='Tiramisu Recipes!!!',
149
             content_api.save(tiramisu_page)
149
             content_api.save(tiramisu_page)
150
 
150
 
151
         best_cake_thread = content_api.create(
151
         best_cake_thread = content_api.create(
152
-            content_type=ContentType.Thread,
152
+            content_type_slug=CONTENT_TYPES.Thread.slug,
153
             workspace=recipe_workspace,
153
             workspace=recipe_workspace,
154
             parent=dessert_folder,
154
             parent=dessert_folder,
155
             label='Best Cake',
155
             label='Best Cake',
159
         best_cake_thread.description = 'Which is the best cake?'
159
         best_cake_thread.description = 'Which is the best cake?'
160
         self._session.add(best_cake_thread)
160
         self._session.add(best_cake_thread)
161
         apple_pie_recipe = content_api.create(
161
         apple_pie_recipe = content_api.create(
162
-            content_type=ContentType.File,
162
+            content_type_slug=CONTENT_TYPES.File.slug,
163
             workspace=recipe_workspace,
163
             workspace=recipe_workspace,
164
             parent=dessert_folder,
164
             parent=dessert_folder,
165
             label='Apple_Pie',
165
             label='Apple_Pie',
174
         )
174
         )
175
         self._session.add(apple_pie_recipe)
175
         self._session.add(apple_pie_recipe)
176
         Brownie_recipe = content_api.create(
176
         Brownie_recipe = content_api.create(
177
-            content_type=ContentType.File,
177
+            content_type_slug=CONTENT_TYPES.File.slug,
178
             workspace=recipe_workspace,
178
             workspace=recipe_workspace,
179
             parent=dessert_folder,
179
             parent=dessert_folder,
180
             label='Brownie Recipe',
180
             label='Brownie Recipe',
189
         )
189
         )
190
         self._session.add(Brownie_recipe)
190
         self._session.add(Brownie_recipe)
191
         fruits_desserts_folder = content_api.create(
191
         fruits_desserts_folder = content_api.create(
192
-            content_type=ContentType.Folder,
192
+            content_type_slug=CONTENT_TYPES.Folder.slug,
193
             workspace=recipe_workspace,
193
             workspace=recipe_workspace,
194
             label='Fruits Desserts',
194
             label='Fruits Desserts',
195
             parent=dessert_folder,
195
             parent=dessert_folder,
197
         )
197
         )
198
 
198
 
199
         menu_page = content_api.create(
199
         menu_page = content_api.create(
200
-            content_type=ContentType.Page,
200
+            content_type_slug=CONTENT_TYPES.Page.slug,
201
             workspace=business_workspace,
201
             workspace=business_workspace,
202
             parent=menu_workspace,
202
             parent=menu_workspace,
203
             label='Current Menu',
203
             label='Current Menu',
205
         )
205
         )
206
 
206
 
207
         new_fruit_salad = content_api.create(
207
         new_fruit_salad = content_api.create(
208
-            content_type=ContentType.Page,
208
+            content_type_slug=CONTENT_TYPES.Page.slug,
209
             workspace=recipe_workspace,
209
             workspace=recipe_workspace,
210
             parent=fruits_desserts_folder,
210
             parent=fruits_desserts_folder,
211
             label='New Fruit Salad',
211
             label='New Fruit Salad',
212
             do_save=True,
212
             do_save=True,
213
         )
213
         )
214
         old_fruit_salad = content_api.create(
214
         old_fruit_salad = content_api.create(
215
-            content_type=ContentType.Page,
215
+            content_type_slug=CONTENT_TYPES.Page.slug,
216
             workspace=recipe_workspace,
216
             workspace=recipe_workspace,
217
             parent=fruits_desserts_folder,
217
             parent=fruits_desserts_folder,
218
             label='Fruit Salad',
218
             label='Fruit Salad',
228
         content_api.save(old_fruit_salad)
228
         content_api.save(old_fruit_salad)
229
 
229
 
230
         bad_fruit_salad = content_api.create(
230
         bad_fruit_salad = content_api.create(
231
-            content_type=ContentType.Page,
231
+            content_type_slug=CONTENT_TYPES.Page.slug,
232
             workspace=recipe_workspace,
232
             workspace=recipe_workspace,
233
             parent=fruits_desserts_folder,
233
             parent=fruits_desserts_folder,
234
             label='Bad Fruit Salad',
234
             label='Bad Fruit Salad',
245
 
245
 
246
         # File at the root for test
246
         # File at the root for test
247
         new_fruit_salad = content_api.create(
247
         new_fruit_salad = content_api.create(
248
-            content_type=ContentType.Page,
248
+            content_type_slug=CONTENT_TYPES.Page.slug,
249
             workspace=other_workspace,
249
             workspace=other_workspace,
250
             label='New Fruit Salad',
250
             label='New Fruit Salad',
251
             do_save=True,
251
             do_save=True,
252
         )
252
         )
253
         old_fruit_salad = content_api.create(
253
         old_fruit_salad = content_api.create(
254
-            content_type=ContentType.Page,
254
+            content_type_slug=CONTENT_TYPES.Page.slug,
255
             workspace=other_workspace,
255
             workspace=other_workspace,
256
             label='Fruit Salad',
256
             label='Fruit Salad',
257
             do_save=True,
257
             do_save=True,
265
         content_api.save(old_fruit_salad)
265
         content_api.save(old_fruit_salad)
266
 
266
 
267
         bad_fruit_salad = content_api.create(
267
         bad_fruit_salad = content_api.create(
268
-            content_type=ContentType.Page,
268
+            content_type_slug=CONTENT_TYPES.Page.slug,
269
             workspace=other_workspace,
269
             workspace=other_workspace,
270
             label='Bad Fruit Salad',
270
             label='Bad Fruit Salad',
271
             do_save=True,
271
             do_save=True,

+ 65 - 59
backend/tracim_backend/lib/core/content.py View File

34
 from tracim_backend.exceptions import ContentNotFound
34
 from tracim_backend.exceptions import ContentNotFound
35
 from tracim_backend.exceptions import WorkspacesDoNotMatch
35
 from tracim_backend.exceptions import WorkspacesDoNotMatch
36
 from tracim_backend.lib.utils.utils import current_date_for_filename
36
 from tracim_backend.lib.utils.utils import current_date_for_filename
37
+from tracim_backend.models.contents import CONTENT_STATUS
38
+from tracim_backend.models.contents import ContentType
39
+from tracim_backend.models.contents import CONTENT_TYPES
37
 from tracim_backend.models.revision_protection import new_revision
40
 from tracim_backend.models.revision_protection import new_revision
38
 from tracim_backend.models.auth import User
41
 from tracim_backend.models.auth import User
39
 from tracim_backend.models.data import ActionDescription
42
 from tracim_backend.models.data import ActionDescription
40
-from tracim_backend.models.data import ContentStatus
41
 from tracim_backend.models.data import ContentRevisionRO
43
 from tracim_backend.models.data import ContentRevisionRO
42
 from tracim_backend.models.data import Content
44
 from tracim_backend.models.data import Content
43
-from tracim_backend.models.data import ContentType
45
+
44
 from tracim_backend.models.data import NodeTreeItem
46
 from tracim_backend.models.data import NodeTreeItem
45
 from tracim_backend.models.data import RevisionReadStatus
47
 from tracim_backend.models.data import RevisionReadStatus
46
 from tracim_backend.models.data import UserRoleInWorkspace
48
 from tracim_backend.models.data import UserRoleInWorkspace
75
     else:
77
     else:
76
         # TODO - D.A. - 2014-12-02 - Manage Content Types Dynamically
78
         # TODO - D.A. - 2014-12-02 - Manage Content Types Dynamically
77
         content_type_order = [
79
         content_type_order = [
78
-            ContentType.Folder,
79
-            ContentType.Page,
80
-            ContentType.Thread,
81
-            ContentType.File,
80
+            CONTENT_TYPES.Folder.slug,
81
+            CONTENT_TYPES.Page.slug,
82
+            CONTENT_TYPES.Thread.slug,
83
+            CONTENT_TYPES.File.slug,
82
         ]
84
         ]
83
 
85
 
84
         content_1_type_index = content_type_order.index(content1.type)
86
         content_1_type_index = content_type_order.index(content1.type)
105
     SEARCH_SEPARATORS = ',| '
107
     SEARCH_SEPARATORS = ',| '
106
     SEARCH_DEFAULT_RESULT_NB = 50
108
     SEARCH_DEFAULT_RESULT_NB = 50
107
 
109
 
108
-    DISPLAYABLE_CONTENTS = (
109
-        ContentType.Folder,
110
-        ContentType.File,
111
-        ContentType.Comment,
112
-        ContentType.Thread,
113
-        ContentType.Page,
114
-        ContentType.PageLegacy,
115
-        ContentType.MarkdownPage,
116
-    )
110
+    # DISPLAYABLE_CONTENTS = (
111
+    #     CONTENT_TYPES.Folder.slug,
112
+    #     CONTENT_TYPES.File.slug,
113
+    #     CONTENT_TYPES.Comment.slug,
114
+    #     CONTENT_TYPES.Thread.slug,
115
+    #     CONTENT_TYPES.Page.slug,
116
+    #     CONTENT_TYPES.Page.slugLegacy,
117
+    #     ContentType.MarkdownPage,
118
+    # )
117
 
119
 
118
     def __init__(
120
     def __init__(
119
             self,
121
             self,
230
 
232
 
231
         # Exclude non displayable types
233
         # Exclude non displayable types
232
         if not self._force_show_all_types:
234
         if not self._force_show_all_types:
233
-            result = result.filter(Content.type.in_(self.DISPLAYABLE_CONTENTS))
235
+            result = result.filter(Content.type.in_(CONTENT_TYPES.query_allowed_types_slugs()))
234
 
236
 
235
         if workspace:
237
         if workspace:
236
             result = result.filter(Content.workspace_id == workspace.workspace_id)
238
             result = result.filter(Content.workspace_id == workspace.workspace_id)
369
     #     removed_item_ids = removed_item_ids or []  # FDV
371
     #     removed_item_ids = removed_item_ids or []  # FDV
370
     # 
372
     # 
371
     #     if not allowed_node_types:
373
     #     if not allowed_node_types:
372
-    #         allowed_node_types = [ContentType.Folder]
373
-    #     elif allowed_node_types==ContentType.Any:
374
+    #         allowed_node_types = [CONTENT_TYPES.Folder.slug]
375
+    #     elif allowed_node_types==CONTENT_TYPES.Any_SLUG:
374
     #         allowed_node_types = ContentType.all()
376
     #         allowed_node_types = ContentType.all()
375
     # 
377
     # 
376
     #     parent_id = parent.content_id if parent else None
378
     #     parent_id = parent.content_id if parent else None
395
     #     for folder in folders:
397
     #     for folder in folders:
396
     #         for allowed_content_type in filter_by_allowed_content_types:
398
     #         for allowed_content_type in filter_by_allowed_content_types:
397
     # 
399
     # 
398
-    #             is_folder = folder.type == ContentType.Folder
400
+    #             is_folder = folder.type == CONTENT_TYPES.Folder.slug
399
     #             content_type__allowed = folder.properties['allowed_content'][allowed_content_type] == True
401
     #             content_type__allowed = folder.properties['allowed_content'][allowed_content_type] == True
400
     # 
402
     # 
401
     #             if is_folder and content_type__allowed:
403
     #             if is_folder and content_type__allowed:
404
     # 
406
     # 
405
     #     return result
407
     #     return result
406
 
408
 
407
-    def create(self, content_type: str, workspace: Workspace, parent: Content=None, label: str ='', filename: str = '', do_save=False, is_temporary: bool=False, do_notify=True) -> Content:
409
+    def create(self, content_type_slug: str, workspace: Workspace, parent: Content=None, label: str = '', filename: str = '', do_save=False, is_temporary: bool=False, do_notify=True) -> Content:
408
         # TODO - G.M - 2018-07-16 - raise Exception instead of assert
410
         # TODO - G.M - 2018-07-16 - raise Exception instead of assert
409
-        assert content_type in ContentType.allowed_types()
411
+        assert content_type_slug in CONTENT_TYPES.query_allowed_types_slugs()
412
+        assert content_type_slug != CONTENT_TYPES.Any_SLUG
410
         assert not (label and filename)
413
         assert not (label and filename)
411
 
414
 
412
-        if content_type == ContentType.Folder and not label:
415
+        if content_type_slug == CONTENT_TYPES.Folder.slug and not label:
413
             label = self.generate_folder_label(workspace, parent)
416
             label = self.generate_folder_label(workspace, parent)
414
 
417
 
415
         content = Content()
418
         content = Content()
421
         elif label:
424
         elif label:
422
             content.label = label
425
             content.label = label
423
         else:
426
         else:
424
-            if content_type == ContentType.Comment:
427
+            if content_type_slug == CONTENT_TYPES.Comment.slug:
425
                 # INFO - G.M - 2018-07-16 - Default label for comments is
428
                 # INFO - G.M - 2018-07-16 - Default label for comments is
426
                 # empty string.
429
                 # empty string.
427
                 content.label = ''
430
                 content.label = ''
431
         content.owner = self._user
434
         content.owner = self._user
432
         content.parent = parent
435
         content.parent = parent
433
         content.workspace = workspace
436
         content.workspace = workspace
434
-        content.type = content_type
437
+        content.type = content_type_slug
435
         content.is_temporary = is_temporary
438
         content.is_temporary = is_temporary
436
         content.revision_type = ActionDescription.CREATION
439
         content.revision_type = ActionDescription.CREATION
437
 
440
 
438
         if content.type in (
441
         if content.type in (
439
-                ContentType.Page,
440
-                ContentType.Thread,
442
+                CONTENT_TYPES.Page.slug,
443
+                CONTENT_TYPES.Thread.slug,
441
         ):
444
         ):
442
             content.file_extension = '.html'
445
             content.file_extension = '.html'
443
 
446
 
447
         return content
450
         return content
448
 
451
 
449
     def create_comment(self, workspace: Workspace=None, parent: Content=None, content:str ='', do_save=False) -> Content:
452
     def create_comment(self, workspace: Workspace=None, parent: Content=None, content:str ='', do_save=False) -> Content:
450
-        assert parent and parent.type != ContentType.Folder
453
+        assert parent and parent.type != CONTENT_TYPES.Folder.slug
451
         if not content:
454
         if not content:
452
             raise EmptyCommentContentNotAllowed()
455
             raise EmptyCommentContentNotAllowed()
453
         item = Content()
456
         item = Content()
456
         if not workspace:
459
         if not workspace:
457
             workspace = item.parent.workspace
460
             workspace = item.parent.workspace
458
         item.workspace = workspace
461
         item.workspace = workspace
459
-        item.type = ContentType.Comment
462
+        item.type = CONTENT_TYPES.Comment.slug
460
         item.description = content
463
         item.description = content
461
         item.label = ''
464
         item.label = ''
462
         item.revision_type = ActionDescription.COMMENT
465
         item.revision_type = ActionDescription.COMMENT
492
 
495
 
493
         base_request = self._base_query(workspace).filter(Content.content_id==content_id)
496
         base_request = self._base_query(workspace).filter(Content.content_id==content_id)
494
 
497
 
495
-        if content_type!=ContentType.Any:
498
+        if content_type!=CONTENT_TYPES.Any_SLUG:
496
             base_request = base_request.filter(Content.type==content_type)
499
             base_request = base_request.filter(Content.type==content_type)
497
 
500
 
498
         if parent:
501
         if parent:
577
         return query.filter(
580
         return query.filter(
578
             or_(
581
             or_(
579
                 and_(
582
                 and_(
580
-                    Content.type == ContentType.File,
583
+                    Content.type == CONTENT_TYPES.File.slug,
581
                     Content.label == file_name,
584
                     Content.label == file_name,
582
                     Content.file_extension == file_extension,
585
                     Content.file_extension == file_extension,
583
                 ),
586
                 ),
584
                 and_(
587
                 and_(
585
-                    Content.type == ContentType.Thread,
588
+                    Content.type == CONTENT_TYPES.Thread.slug,
586
                     Content.label == file_name,
589
                     Content.label == file_name,
587
                 ),
590
                 ),
588
                 and_(
591
                 and_(
589
-                    Content.type == ContentType.Page,
592
+                    Content.type == CONTENT_TYPES.Page.slug,
590
                     Content.label == file_name,
593
                     Content.label == file_name,
591
                 ),
594
                 ),
592
                 and_(
595
                 and_(
593
-                    Content.type == ContentType.Folder,
596
+                    Content.type == CONTENT_TYPES.Folder.slug,
594
                     Content.label == content_label,
597
                     Content.label == content_label,
595
                 ),
598
                 ),
596
             )
599
             )
671
             # Filter query on label
674
             # Filter query on label
672
             folder_query = query \
675
             folder_query = query \
673
                 .filter(
676
                 .filter(
674
-                    Content.type == ContentType.Folder,
677
+                    Content.type == CONTENT_TYPES.Folder.slug,
675
                     Content.label == label,
678
                     Content.label == label,
676
                     Content.workspace_id == workspace.workspace_id,
679
                     Content.workspace_id == workspace.workspace_id,
677
                 )
680
                 )
723
 
726
 
724
         return query.filter(or_(
727
         return query.filter(or_(
725
             and_(
728
             and_(
726
-                Content.type == ContentType.File,
729
+                Content.type == CONTENT_TYPES.File.slug,
727
                 file_name_filter,
730
                 file_name_filter,
728
                 file_extension_filter,
731
                 file_extension_filter,
729
             ),
732
             ),
730
             and_(
733
             and_(
731
-                Content.type == ContentType.Thread,
734
+                Content.type == CONTENT_TYPES.Thread.slug,
732
                 file_name_filter,
735
                 file_name_filter,
733
                 file_extension_filter,
736
                 file_extension_filter,
734
             ),
737
             ),
735
             and_(
738
             and_(
736
-                Content.type == ContentType.Page,
739
+                Content.type == CONTENT_TYPES.Page.slug,
737
                 file_name_filter,
740
                 file_name_filter,
738
                 file_extension_filter,
741
                 file_extension_filter,
739
             ),
742
             ),
740
             and_(
743
             and_(
741
-                Content.type == ContentType.Folder,
744
+                Content.type == CONTENT_TYPES.Folder.slug,
742
                 label_filter,
745
                 label_filter,
743
             ),
746
             ),
744
         ))
747
         ))
844
     def _get_all_query(
847
     def _get_all_query(
845
         self,
848
         self,
846
         parent_id: int = None,
849
         parent_id: int = None,
847
-        content_type: str = ContentType.Any,
850
+        content_type_slug: str = CONTENT_TYPES.Any_SLUG,
848
         workspace: Workspace = None
851
         workspace: Workspace = None
849
     ) -> Query:
852
     ) -> Query:
850
         """
853
         """
851
         Extended filter for better "get all data" query
854
         Extended filter for better "get all data" query
852
         :param parent_id: filter by parent_id
855
         :param parent_id: filter by parent_id
853
-        :param content_type: filter by content_type slug
856
+        :param content_type_slug: filter by content_type slug
854
         :param workspace: filter by workspace
857
         :param workspace: filter by workspace
855
         :return:
858
         :return:
856
         """
859
         """
857
         assert parent_id is None or isinstance(parent_id, int)
860
         assert parent_id is None or isinstance(parent_id, int)
858
-        assert content_type is not None
861
+        assert content_type_slug is not None
859
         resultset = self._base_query(workspace)
862
         resultset = self._base_query(workspace)
860
 
863
 
861
-        if content_type!=ContentType.Any:
864
+        if content_type_slug != CONTENT_TYPES.Any_SLUG:
862
             # INFO - G.M - 2018-07-05 - convert with
865
             # INFO - G.M - 2018-07-05 - convert with
863
             #  content type object to support legacy slug
866
             #  content type object to support legacy slug
864
-            content_type_object = ContentType(content_type)
865
-            resultset = resultset.filter(Content.type.in_(content_type_object.get_slug_aliases()))
867
+            content_type_object = CONTENT_TYPES.get_one_by_slug(content_type_slug)
868
+            all_slug_alias = [content_type_object.slug]
869
+            if content_type_object.slug_alias:
870
+                all_slug_alias.extend(content_type_object.slug_alias)
871
+            resultset = resultset.filter(Content.type.in_(all_slug_alias))
866
 
872
 
867
         if parent_id:
873
         if parent_id:
868
             resultset = resultset.filter(Content.parent_id==parent_id)
874
             resultset = resultset.filter(Content.parent_id==parent_id)
871
 
877
 
872
         return resultset
878
         return resultset
873
 
879
 
874
-    def get_all(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> typing.List[Content]:
880
+    def get_all(self, parent_id: int=None, content_type: str=CONTENT_TYPES.Any_SLUG, workspace: Workspace=None) -> typing.List[Content]:
875
         return self._get_all_query(parent_id, content_type, workspace).all()
881
         return self._get_all_query(parent_id, content_type, workspace).all()
876
 
882
 
877
     # TODO - G.M - 2018-07-17 - [Cleanup] Drop this method if unneeded
883
     # TODO - G.M - 2018-07-17 - [Cleanup] Drop this method if unneeded
895
 
901
 
896
     # TODO - G.M - 2018-07-17 - [Cleanup] Drop this method if unneeded
902
     # TODO - G.M - 2018-07-17 - [Cleanup] Drop this method if unneeded
897
     # TODO find an other name to filter on is_deleted / is_archived
903
     # TODO find an other name to filter on is_deleted / is_archived
898
-    def get_all_with_filter(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> typing.List[Content]:
904
+    def get_all_with_filter(self, parent_id: int=None, content_type: str=CONTENT_TYPES.Any_SLUG, workspace: Workspace=None) -> typing.List[Content]:
899
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
905
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
900
         assert content_type is not None# DYN_REMOVE
906
         assert content_type is not None# DYN_REMOVE
901
         assert isinstance(content_type, str) # DYN_REMOVE
907
         assert isinstance(content_type, str) # DYN_REMOVE
902
 
908
 
903
         resultset = self._base_query(workspace)
909
         resultset = self._base_query(workspace)
904
 
910
 
905
-        if content_type != ContentType.Any:
911
+        if content_type != CONTENT_TYPES.Any_SLUG:
906
             resultset = resultset.filter(Content.type==content_type)
912
             resultset = resultset.filter(Content.type==content_type)
907
 
913
 
908
         resultset = resultset.filter(Content.is_deleted == self._show_deleted)
914
         resultset = resultset.filter(Content.is_deleted == self._show_deleted)
919
 
925
 
920
         resultset = self._base_query(workspace)
926
         resultset = self._base_query(workspace)
921
 
927
 
922
-        if content_type != ContentType.Any:
928
+        if content_type != CONTENT_TYPES.Any_SLUG:
923
             resultset = resultset.filter(Content.type==content_type)
929
             resultset = resultset.filter(Content.type==content_type)
924
 
930
 
925
         return resultset.all()
931
         return resultset.all()
945
     #
951
     #
946
     #     resultset = self._base_query(workspace)
952
     #     resultset = self._base_query(workspace)
947
     #
953
     #
948
-    #     if content_type != ContentType.Any:
954
+    #     if content_type != CONTENT_TYPES.Any_SLUG:
949
     #         resultset = resultset.filter(Content.type==content_type)
955
     #         resultset = resultset.filter(Content.type==content_type)
950
     #
956
     #
951
     #     return resultset.all()
957
     #     return resultset.all()
978
                     Content.content_id.in_(content_ids),
984
                     Content.content_id.in_(content_ids),
979
                     and_(
985
                     and_(
980
                         Content.parent_id.in_(content_ids),
986
                         Content.parent_id.in_(content_ids),
981
-                        Content.type == ContentType.Comment
987
+                        Content.type == CONTENT_TYPES.Comment.slug
982
                     )
988
                     )
983
                 )
989
                 )
984
             )
990
             )
990
         before_content_find = False
996
         before_content_find = False
991
         for content in resultset:
997
         for content in resultset:
992
             related_active_content = None
998
             related_active_content = None
993
-            if ContentType.Comment == content.type:
999
+            if CONTENT_TYPES.Comment.slug == content.type:
994
                 related_active_content = content.parent
1000
                 related_active_content = content.parent
995
             else:
1001
             else:
996
                 related_active_content = content
1002
                 related_active_content = content
1040
     #         .filter(Content.content_id.in_(not_read_content_ids)) \
1046
     #         .filter(Content.content_id.in_(not_read_content_ids)) \
1041
     #         .order_by(desc(Content.updated))
1047
     #         .order_by(desc(Content.updated))
1042
     #
1048
     #
1043
-    #     if content_type != ContentType.Any:
1049
+    #     if content_type != CONTENT_TYPES.Any_SLUG:
1044
     #         not_read_contents = not_read_contents.filter(
1050
     #         not_read_contents = not_read_contents.filter(
1045
     #             Content.type==content_type)
1051
     #             Content.type==content_type)
1046
     #     else:
1052
     #     else:
1047
     #         not_read_contents = not_read_contents.filter(
1053
     #         not_read_contents = not_read_contents.filter(
1048
-    #             Content.type!=ContentType.Folder)
1054
+    #             Content.type!=CONTENT_TYPES.Folder.slug)
1049
     #
1055
     #
1050
     #     if parent_id:
1056
     #     if parent_id:
1051
     #         not_read_contents = not_read_contents.filter(
1057
     #         not_read_contents = not_read_contents.filter(
1054
     #     result = []
1060
     #     result = []
1055
     #     for item in not_read_contents:
1061
     #     for item in not_read_contents:
1056
     #         new_item = None
1062
     #         new_item = None
1057
-    #         if ContentType.Comment == item.type:
1063
+    #         if CONTENT_TYPES.Comment.slug == item.type:
1058
     #             new_item = item.parent
1064
     #             new_item = item.parent
1059
     #         else:
1065
     #         else:
1060
     #             new_item = item
1066
     #             new_item = item
1086
         folder.properties = properties
1092
         folder.properties = properties
1087
 
1093
 
1088
     def set_status(self, content: Content, new_status: str):
1094
     def set_status(self, content: Content, new_status: str):
1089
-        if new_status in ContentStatus.allowed_values():
1095
+        if new_status in CONTENT_STATUS.get_all_slugs_values():
1090
             content.status = new_status
1096
             content.status = new_status
1091
             content.revision_type = ActionDescription.STATUS_UPDATE
1097
             content.revision_type = ActionDescription.STATUS_UPDATE
1092
         else:
1098
         else:
1296
                 self.mark_read(child, read_datetime=read_datetime,
1302
                 self.mark_read(child, read_datetime=read_datetime,
1297
                                do_flush=False)
1303
                                do_flush=False)
1298
 
1304
 
1299
-            if ContentType.Comment == content.type:
1305
+            if CONTENT_TYPES.Comment.slug == content.type:
1300
                 self.mark_read(content.parent, read_datetime=read_datetime,
1306
                 self.mark_read(content.parent, read_datetime=read_datetime,
1301
                                do_flush=False, recursive=False)
1307
                                do_flush=False, recursive=False)
1302
                 for comment in content.parent.get_comments():
1308
                 for comment in content.parent.get_comments():
1416
         return title_keyworded_items
1422
         return title_keyworded_items
1417
 
1423
 
1418
     def get_all_types(self) -> typing.List[ContentType]:
1424
     def get_all_types(self) -> typing.List[ContentType]:
1419
-        labels = ContentType.all()
1425
+        labels = CONTENT_TYPES.endpoint_allowed_types_slug()
1420
         content_types = []
1426
         content_types = []
1421
         for label in labels:
1427
         for label in labels:
1422
-            content_types.append(ContentType(label))
1428
+            content_types.append(CONTENT_TYPES.get_one_by_slug(label))
1423
 
1429
 
1424
-        return ContentType.sorted(content_types)
1430
+        return content_types
1425
 
1431
 
1426
     # TODO - G.M - 2018-07-24 - [Cleanup] Is this method already needed ?
1432
     # TODO - G.M - 2018-07-24 - [Cleanup] Is this method already needed ?
1427
     def exclude_unavailable(
1433
     def exclude_unavailable(

+ 1 - 1
backend/tracim_backend/lib/core/notifications.py View File

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
 from sqlalchemy.orm import Session
2
 from sqlalchemy.orm import Session
3
 
3
 
4
-from tracim_backend import CFG
4
+from tracim_backend.config import CFG
5
 from tracim_backend.lib.utils.logger import logger
5
 from tracim_backend.lib.utils.logger import logger
6
 from tracim_backend.models.auth import User
6
 from tracim_backend.models.auth import User
7
 from tracim_backend.models.data import Content
7
 from tracim_backend.models.data import Content

+ 41 - 3
backend/tracim_backend/lib/core/user.py View File

3
 
3
 
4
 import transaction
4
 import transaction
5
 import typing as typing
5
 import typing as typing
6
+
7
+from sqlalchemy import or_
6
 from sqlalchemy.orm import Session
8
 from sqlalchemy.orm import Session
9
+from sqlalchemy.orm import Query
7
 from sqlalchemy.orm.exc import NoResultFound
10
 from sqlalchemy.orm.exc import NoResultFound
8
 
11
 
9
-from tracim_backend import CFG
12
+from tracim_backend.config import CFG
10
 from tracim_backend.models.auth import User
13
 from tracim_backend.models.auth import User
11
 from tracim_backend.models.auth import Group
14
 from tracim_backend.models.auth import Group
12
 from tracim_backend.exceptions import NoUserSetted
15
 from tracim_backend.exceptions import NoUserSetted
16
+from tracim_backend.exceptions import TooShortAutocompleteString
13
 from tracim_backend.exceptions import PasswordDoNotMatch
17
 from tracim_backend.exceptions import PasswordDoNotMatch
14
 from tracim_backend.exceptions import EmailValidationFailed
18
 from tracim_backend.exceptions import EmailValidationFailed
15
 from tracim_backend.exceptions import UserDoesNotExist
19
 from tracim_backend.exceptions import UserDoesNotExist
20
 from tracim_backend.models.context_models import UserInContext
24
 from tracim_backend.models.context_models import UserInContext
21
 from tracim_backend.lib.mail_notifier.notifier import get_email_manager
25
 from tracim_backend.lib.mail_notifier.notifier import get_email_manager
22
 from tracim_backend.models.context_models import TypeUser
26
 from tracim_backend.models.context_models import TypeUser
27
+from tracim_backend.models.data import UserRoleInWorkspace
23
 
28
 
24
 
29
 
25
 class UserApi(object):
30
 class UserApi(object):
94
             raise UserDoesNotExist('There is no current user')
99
             raise UserDoesNotExist('There is no current user')
95
         return self._user
100
         return self._user
96
 
101
 
102
+    def _get_all_query(self) -> Query:
103
+        return self._session.query(User).order_by(User.display_name)
104
+
97
     def get_all(self) -> typing.Iterable[User]:
105
     def get_all(self) -> typing.Iterable[User]:
98
-        return self._session.query(User).order_by(User.display_name).all()
106
+        return self._get_all_query().all()
107
+
108
+    def get_known_user(
109
+            self,
110
+            acp: str,
111
+    ) -> typing.Iterable[User]:
112
+        """
113
+        Return list of know user by current UserApi user.
114
+        :param acp: autocomplete filter by name/email
115
+        :return: List of found users
116
+        """
117
+        if len(acp) < 2:
118
+            raise TooShortAutocompleteString(
119
+                '"{acp}" is a too short string, acp string need to have more than one character'.format(acp=acp)  # nopep8
120
+            )
121
+        query = self._get_all_query()
122
+        query = query.filter(or_(User.display_name.ilike('%{}%'.format(acp)), User.email.ilike('%{}%'.format(acp))))  # nopep8
123
+
124
+        # INFO - G.M - 2018-07-27 - if user is set and is simple user, we
125
+        # should show only user in same workspace as user
126
+        if self._user and self._user.profile.id <= Group.TIM_USER:
127
+            user_workspaces_id_query = self._session.\
128
+                query(UserRoleInWorkspace.workspace_id).\
129
+                distinct(UserRoleInWorkspace.workspace_id).\
130
+                filter(UserRoleInWorkspace.user_id == self._user.user_id)
131
+            users_in_workspaces = self._session.\
132
+                query(UserRoleInWorkspace.user_id).\
133
+                distinct(UserRoleInWorkspace.user_id).\
134
+                filter(UserRoleInWorkspace.workspace_id.in_(user_workspaces_id_query.subquery())).subquery()  # nopep8
135
+            query = query.filter(User.user_id.in_(users_in_workspaces))
136
+        return query.all()
99
 
137
 
100
     def find(
138
     def find(
101
             self,
139
             self,
196
         )
234
         )
197
         if do_save:
235
         if do_save:
198
             # TODO - G.M - 2018-07-24 - Check why commit is needed here
236
             # TODO - G.M - 2018-07-24 - Check why commit is needed here
199
-            transaction.commit()
237
+            self.save(user)
200
         return user
238
         return user
201
 
239
 
202
     def set_email(
240
     def set_email(

+ 1 - 1
backend/tracim_backend/lib/core/userworkspace.py View File

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
 import typing
2
 import typing
3
 
3
 
4
-from tracim_backend import CFG
4
+from tracim_backend.config import CFG
5
 from tracim_backend.models.context_models import UserRoleWorkspaceInContext
5
 from tracim_backend.models.context_models import UserRoleWorkspaceInContext
6
 from tracim_backend.models.roles import WorkspaceRoles
6
 from tracim_backend.models.roles import WorkspaceRoles
7
 
7
 

+ 1 - 1
backend/tracim_backend/lib/core/workspace.py View File

5
 from sqlalchemy.orm import Session
5
 from sqlalchemy.orm import Session
6
 from sqlalchemy.orm.exc import NoResultFound
6
 from sqlalchemy.orm.exc import NoResultFound
7
 
7
 
8
-from tracim_backend import CFG
8
+from tracim_backend.config import CFG
9
 from tracim_backend.exceptions import EmptyLabelNotAllowed
9
 from tracim_backend.exceptions import EmptyLabelNotAllowed
10
 from tracim_backend.exceptions import WorkspaceNotFound
10
 from tracim_backend.exceptions import WorkspaceNotFound
11
 from tracim_backend.lib.utils.translation import fake_translator as _
11
 from tracim_backend.lib.utils.translation import fake_translator as _

+ 46 - 26
backend/tracim_backend/lib/mail_notifier/notifier.py View File

10
 from mako.template import Template
10
 from mako.template import Template
11
 from sqlalchemy.orm import Session
11
 from sqlalchemy.orm import Session
12
 
12
 
13
-from tracim_backend import CFG
13
+from tracim_backend.config import CFG
14
 from tracim_backend.lib.core.notifications import INotifier
14
 from tracim_backend.lib.core.notifications import INotifier
15
 from tracim_backend.lib.mail_notifier.sender import EmailSender
15
 from tracim_backend.lib.mail_notifier.sender import EmailSender
16
 from tracim_backend.lib.mail_notifier.utils import SmtpConfiguration, EST
16
 from tracim_backend.lib.mail_notifier.utils import SmtpConfiguration, EST
17
 from tracim_backend.lib.mail_notifier.sender import send_email_through
17
 from tracim_backend.lib.mail_notifier.sender import send_email_through
18
 from tracim_backend.lib.core.workspace import WorkspaceApi
18
 from tracim_backend.lib.core.workspace import WorkspaceApi
19
 from tracim_backend.lib.utils.logger import logger
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
 from tracim_backend.models.auth import User
22
 from tracim_backend.models.auth import User
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
22
 from tracim_backend.models.data import ActionDescription
26
 from tracim_backend.models.data import ActionDescription
23
 from tracim_backend.models.data import Content
27
 from tracim_backend.models.data import Content
24
-from tracim_backend.models.data import ContentType
25
 from tracim_backend.models.data import UserRoleInWorkspace
28
 from tracim_backend.models.data import UserRoleInWorkspace
26
 from tracim_backend.lib.utils.translation import fake_translator as l_, \
29
 from tracim_backend.lib.utils.translation import fake_translator as l_, \
27
     fake_translator as _
30
     fake_translator as _
233
             config=self.config,
236
             config=self.config,
234
             show_archived=True,
237
             show_archived=True,
235
             show_deleted=True,
238
             show_deleted=True,
236
-        ).get_one(event_content_id, ContentType.Any)
237
-        main_content = content.parent if content.type == ContentType.Comment else content
239
+        ).get_one(event_content_id, CONTENT_TYPES.Any_SLUG)
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
         notifiable_roles = WorkspaceApi(
247
         notifiable_roles = WorkspaceApi(
239
             current_user=user,
248
             current_user=user,
240
             session=self.session,
249
             session=self.session,
265
             # INFO - G.M - 2017-11-15 - set content_id in header to permit reply
274
             # INFO - G.M - 2017-11-15 - set content_id in header to permit reply
266
             # references can have multiple values, but only one in this case.
275
             # references can have multiple values, but only one in this case.
267
             replyto_addr = self.config.EMAIL_NOTIFICATION_REPLY_TO_EMAIL.replace( # nopep8
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
             reference_addr = self.config.EMAIL_NOTIFICATION_REFERENCES_EMAIL.replace( #nopep8
280
             reference_addr = self.config.EMAIL_NOTIFICATION_REFERENCES_EMAIL.replace( #nopep8
297
             # To link this email to a content we create a virtual parent
306
             # To link this email to a content we create a virtual parent
298
             # in reference who contain the content_id.
307
             # in reference who contain the content_id.
299
             message['References'] = formataddr(('',reference_addr))
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
             part1 = MIMEText(body_text, 'plain', 'utf-8')
325
             part1 = MIMEText(body_text, 'plain', 'utf-8')
304
             part2 = MIMEText(body_html, 'html', 'utf-8')
326
             part2 = MIMEText(body_html, 'html', 'utf-8')
362
             'user': user,
384
             'user': user,
363
             'password': password,
385
             'password': password,
364
             # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for logo_url  # nopep8
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
             # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for login_url  # nopep8
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
         body_text = self._render_template(
391
         body_text = self._render_template(
370
             mako_template_filepath=text_template_file_path,
392
             mako_template_filepath=text_template_file_path,
415
             self,
437
             self,
416
             mako_template_filepath: str,
438
             mako_template_filepath: str,
417
             role: UserRoleInWorkspace,
439
             role: UserRoleInWorkspace,
418
-            content: Content,
419
-            actor: User
440
+            content_in_context: ContentInContext,
441
+            workspace_in_context: WorkspaceInContext,
442
+            actor: User,
420
     ) -> str:
443
     ) -> str:
421
         """
444
         """
422
         Build an email body and return it as a string
445
         Build an email body and return it as a string
424
         :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
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
         :param content: the content item related to the notification
448
         :param content: the content item related to the notification
426
         :param actor: the user at the origin of the action / notification (for example the one who wrote a comment
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
         :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
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
         logger.debug(self, 'Building email content from MAKO template {}'.format(mako_template_filepath))
452
         logger.debug(self, 'Building email content from MAKO template {}'.format(mako_template_filepath))
431
-
453
+        content = content_in_context.content
432
         main_title = content.label
454
         main_title = content.label
433
         content_intro = ''
455
         content_intro = ''
434
         content_text = ''
456
         content_text = ''
435
         call_to_action_text = ''
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
         # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for status_icon_url  # nopep8
460
         # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for status_icon_url  # nopep8
440
         status_icon_url = ''
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
         # TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for logo_url  # nopep8
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
         action = content.get_last_action().id
466
         action = content.get_last_action().id
447
         if ActionDescription.COMMENT == action:
467
         if ActionDescription.COMMENT == action:
455
             content_text = content.description
475
             content_text = content.description
456
             call_to_action_text = l_('View online')
476
             call_to_action_text = l_('View online')
457
 
477
 
458
-            if ContentType.Thread == content.type:
478
+            if CONTENT_TYPES.Thread.slug == content.type:
459
                 call_to_action_text = l_('Answer')
479
                 call_to_action_text = l_('Answer')
460
                 content_intro = l_('<span id="content-intro-username">{}</span> started a thread entitled:').format(actor.display_name)
480
                 content_intro = l_('<span id="content-intro-username">{}</span> started a thread entitled:').format(actor.display_name)
461
                 content_text = '<p id="content-body-intro">{}</p>'.format(content.label) + \
481
                 content_text = '<p id="content-body-intro">{}</p>'.format(content.label) + \
462
                                content.get_last_comment_from(actor).description
482
                                content.get_last_comment_from(actor).description
463
 
483
 
464
-            elif ContentType.File == content.type:
484
+            elif CONTENT_TYPES.File.slug == content.type:
465
                 content_intro = l_('<span id="content-intro-username">{}</span> added a file entitled:').format(actor.display_name)
485
                 content_intro = l_('<span id="content-intro-username">{}</span> added a file entitled:').format(actor.display_name)
466
                 if content.description:
486
                 if content.description:
467
                     content_text = content.description
487
                     content_text = content.description
468
                 else:
488
                 else:
469
                     content_text = '<span id="content-body-only-title">{}</span>'.format(content.label)
489
                     content_text = '<span id="content-body-only-title">{}</span>'.format(content.label)
470
 
490
 
471
-            elif ContentType.Page == content.type:
491
+            elif CONTENT_TYPES.Page.slug == content.type:
472
                 content_intro = l_('<span id="content-intro-username">{}</span> added a page entitled:').format(actor.display_name)
492
                 content_intro = l_('<span id="content-intro-username">{}</span> added a page entitled:').format(actor.display_name)
473
                 content_text = '<span id="content-body-only-title">{}</span>'.format(content.label)
493
                 content_text = '<span id="content-body-only-title">{}</span>'.format(content.label)
474
 
494
 
476
             content_text = content.description
496
             content_text = content.description
477
             call_to_action_text = l_('View online')
497
             call_to_action_text = l_('View online')
478
 
498
 
479
-            if ContentType.File == content.type:
499
+            if CONTENT_TYPES.File.slug == content.type:
480
                 content_intro = l_('<span id="content-intro-username">{}</span> uploaded a new revision.').format(actor.display_name)
500
                 content_intro = l_('<span id="content-intro-username">{}</span> uploaded a new revision.').format(actor.display_name)
481
                 content_text = ''
501
                 content_text = ''
482
 
502
 
483
-            elif ContentType.Page == content.type:
503
+            elif CONTENT_TYPES.Page.slug == content.type:
484
                 content_intro = l_('<span id="content-intro-username">{}</span> updated this page.').format(actor.display_name)
504
                 content_intro = l_('<span id="content-intro-username">{}</span> updated this page.').format(actor.display_name)
485
                 previous_revision = content.get_previous_revision()
505
                 previous_revision = content.get_previous_revision()
486
                 title_diff = ''
506
                 title_diff = ''
490
                     title_diff + \
510
                     title_diff + \
491
                     htmldiff(previous_revision.description, content.description)
511
                     htmldiff(previous_revision.description, content.description)
492
 
512
 
493
-            elif ContentType.Thread == content.type:
513
+            elif CONTENT_TYPES.Thread.slug == content.type:
494
                 content_intro = l_('<span id="content-intro-username">{}</span> updated the thread description.').format(actor.display_name)
514
                 content_intro = l_('<span id="content-intro-username">{}</span> updated the thread description.').format(actor.display_name)
495
                 previous_revision = content.get_previous_revision()
515
                 previous_revision = content.get_previous_revision()
496
                 title_diff = ''
516
                 title_diff = ''
503
         elif ActionDescription.EDITION == action:
523
         elif ActionDescription.EDITION == action:
504
             call_to_action_text = l_('View online')
524
             call_to_action_text = l_('View online')
505
 
525
 
506
-            if ContentType.File == content.type:
526
+            if CONTENT_TYPES.File.slug == content.type:
507
                 content_intro = l_('<span id="content-intro-username">{}</span> updated the file description.').format(actor.display_name)
527
                 content_intro = l_('<span id="content-intro-username">{}</span> updated the file description.').format(actor.display_name)
508
                 content_text = '<p id="content-body-intro">{}</p>'.format(content.get_label()) + \
528
                 content_text = '<p id="content-body-intro">{}</p>'.format(content.get_label()) + \
509
                     content.description
529
                     content.description

+ 6 - 6
backend/tracim_backend/lib/utils/authorization.py View File

5
 from pyramid.interfaces import IAuthorizationPolicy
5
 from pyramid.interfaces import IAuthorizationPolicy
6
 from zope.interface import implementer
6
 from zope.interface import implementer
7
 
7
 
8
-from tracim_backend.models.contents import NewContentType
9
-from tracim_backend.models.context_models import ContentInContext
8
+from tracim_backend.models.contents import ContentType
9
+from tracim_backend.models.contents import CONTENT_TYPES
10
 
10
 
11
 try:
11
 try:
12
     from json.decoder import JSONDecodeError
12
     from json.decoder import JSONDecodeError
13
 except ImportError:  # python3.4
13
 except ImportError:  # python3.4
14
     JSONDecodeError = ValueError
14
     JSONDecodeError = ValueError
15
 
15
 
16
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
16
+from tracim_backend.models.contents import ContentType
17
 from tracim_backend.exceptions import InsufficientUserRoleInWorkspace
17
 from tracim_backend.exceptions import InsufficientUserRoleInWorkspace
18
 from tracim_backend.exceptions import ContentTypeNotAllowed
18
 from tracim_backend.exceptions import ContentTypeNotAllowed
19
 from tracim_backend.exceptions import InsufficientUserProfile
19
 from tracim_backend.exceptions import InsufficientUserProfile
133
     return decorator
133
     return decorator
134
 
134
 
135
 
135
 
136
-def require_content_types(content_types: typing.List['NewContentType']) -> typing.Callable:  # nopep8
136
+def require_content_types(content_types: typing.List['ContentType']) -> typing.Callable:  # nopep8
137
     """
137
     """
138
     Restricts access to specific file type or raise an exception.
138
     Restricts access to specific file type or raise an exception.
139
     Check role for candidate_workspace.
139
     Check role for candidate_workspace.
140
-    :param content_types: list of NewContentType object
140
+    :param content_types: list of ContentType object
141
     :return: decorator
141
     :return: decorator
142
     """
142
     """
143
     def decorator(func: typing.Callable) -> typing.Callable:
143
     def decorator(func: typing.Callable) -> typing.Callable:
144
         @functools.wraps(func)
144
         @functools.wraps(func)
145
         def wrapper(self, context, request: 'TracimRequest') -> typing.Callable:
145
         def wrapper(self, context, request: 'TracimRequest') -> typing.Callable:
146
             content = request.current_content
146
             content = request.current_content
147
-            current_content_type_slug = ContentType(content.type).slug
147
+            current_content_type_slug = CONTENT_TYPES.get_one_by_slug(content.type).slug
148
             content_types_slug = [content_type.slug for content_type in content_types]  # nopep8
148
             content_types_slug = [content_type.slug for content_type in content_types]  # nopep8
149
             if current_content_type_slug in content_types_slug:
149
             if current_content_type_slug in content_types_slug:
150
                 return func(self, context, request)
150
                 return func(self, context, request)

+ 7 - 3
backend/tracim_backend/lib/utils/request.py View File

15
 from tracim_backend.exceptions import UserDoesNotExist
15
 from tracim_backend.exceptions import UserDoesNotExist
16
 from tracim_backend.exceptions import WorkspaceNotFound
16
 from tracim_backend.exceptions import WorkspaceNotFound
17
 from tracim_backend.exceptions import ImmutableAttribute
17
 from tracim_backend.exceptions import ImmutableAttribute
18
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
18
+from tracim_backend.models.contents import CONTENT_TYPES
19
 from tracim_backend.lib.core.content import ContentApi
19
 from tracim_backend.lib.core.content import ContentApi
20
 from tracim_backend.lib.core.user import UserApi
20
 from tracim_backend.lib.core.user import UserApi
21
 from tracim_backend.lib.core.workspace import WorkspaceApi
21
 from tracim_backend.lib.core.workspace import WorkspaceApi
229
             api = ContentApi(
229
             api = ContentApi(
230
                 current_user=user,
230
                 current_user=user,
231
                 session=request.dbsession,
231
                 session=request.dbsession,
232
+                show_deleted=True,
233
+                show_archived=True,
232
                 config=request.registry.settings['CFG']
234
                 config=request.registry.settings['CFG']
233
             )
235
             )
234
             comment = api.get_one(
236
             comment = api.get_one(
235
                 comment_id,
237
                 comment_id,
236
-                content_type=ContentType.Comment,
238
+                content_type=CONTENT_TYPES.Comment.slug,
237
                 workspace=workspace,
239
                 workspace=workspace,
238
                 parent=content,
240
                 parent=content,
239
             )
241
             )
268
                 raise ContentNotFoundInTracimRequest('No content_id property found in request')  # nopep8
270
                 raise ContentNotFoundInTracimRequest('No content_id property found in request')  # nopep8
269
             api = ContentApi(
271
             api = ContentApi(
270
                 current_user=user,
272
                 current_user=user,
273
+                show_deleted=True,
274
+                show_archived=True,
271
                 session=request.dbsession,
275
                 session=request.dbsession,
272
                 config=request.registry.settings['CFG']
276
                 config=request.registry.settings['CFG']
273
             )
277
             )
274
-            content = api.get_one(content_id=content_id, workspace=workspace, content_type=ContentType.Any)  # nopep8
278
+            content = api.get_one(content_id=content_id, workspace=workspace, content_type=CONTENT_TYPES.Any_SLUG)  # nopep8
275
         except NoResultFound as exc:
279
         except NoResultFound as exc:
276
             raise ContentNotFound(
280
             raise ContentNotFound(
277
                 'Content {} does not exist '
281
                 'Content {} does not exist '

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

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
 import datetime
2
 import datetime
3
+import random
4
+import string
5
+from enum import Enum
6
+
3
 from redis import Redis
7
 from redis import Redis
4
 from rq import Queue
8
 from rq import Queue
5
 
9
 
8
 DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
12
 DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
9
 DEFAULT_WEBDAV_CONFIG_FILE = "wsgidav.conf"
13
 DEFAULT_WEBDAV_CONFIG_FILE = "wsgidav.conf"
10
 DEFAULT_TRACIM_CONFIG_FILE = "development.ini"
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,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4QUTDjMSlsws9AAAB89JREFUaN7tmWtwlFcZx3/PeXeXXLmKQJEGsFZApIUBnaqjUm4VgdYyCUib0cJoKLmHW5EWmlGoQkMICURgEPuhtiYDU7XcyVgKVugULZVCQAwUYyENYAmEXHb3PH7IFjYQNgnZHfnA+bKz+7573v//PPf/C/fWvdWhJWHfMbO4P479OpCAigeowWglPq3ARWfU9AJ7Cb/3MAXZn95dBLKKHsfIQmAUIq7rv6t6gU+BKCAGaEBkN9h88tLe7sgjTdjAJ5Y4OGYMIo80Aw8g4kakJyLxiDiIxABPoMwj5+XP3R0ESpP8WC1H1YuqBj4bUWwIB5gA0d/6/xFQFUpKHBJLHADyU9chTEc1BdEZqJ0O+ofb4xcPyuBIxoBQuHc4ov3w2zrwH0Nd0aBDccwgVPsjEg/4Uc4jegKfvkfMmQ9JSfECkFP0HGJeuu0TrJZhSacg9Xh4Caze2wsXC1B9GugO+BAqUIkC/QIinhYsYoEqYDt+fY2s8WXkFE5CnBIg+vaWtJuQukzy5te2l4Bz2ysTkxchshCRWERMU2BKT4RuiLT0vwagHsENPIyR7zAx2eGybws09AUZEQLHcNS5wMEdB8NjgaKyBNTuQuTLoWOAs6AHUd5DpRzxX0KMBe0M+gA4D4Hdw5mT72LJQORxkM6IfgxSC3wjyHpHEN8M8jKPtYeAq2VgvhHg9A4RvPUIKzCyBVe346SM9LZw1y5KShzOxfUhL60SmMfcNUXgisXYS/iNA/IzhNmBgH4I3E8Cxzpmgby9ffFoMSKTQxA4jZjRpI35qENZbF5Rb1SWgcwM7HsQr28KhZnVd55GPfqTkOCbaB+i2lR1uHa8nHYeda0EygJWGILHGXzndaBgz3CQ5Fb8/hSqr5E7uj4sBXBVSjlqN6FaixKHlS8BxCYtHhs/bUly9JT597U9C30/eRYiU4Nc5Q1gH+AG/gvsxJBH2vg3w9pDPTjqFDGxX0FkKOih2PtHOcbIBiPMMi5XT9eDo9/xlu+rDW2BpX+MAQnOCudRWcWFA3OwncYhrke5VpuK1SrW7+sTVgKv5NYjsr2p6TMY1QEGvhi4mmw89unWXaiX6QQEN1YHuHjpELm5lsxvV2MbhxAdWwCSiNfnCXsb7mc/Ih8hXED8J1T1k6awEAG+R+LCLqEJOL2vInwQOP46VHaSm9QYiI1nMU4xwv2oa3OHs0+LiaHuHKr/wOo/r1yuOQwcDEqVw2KdqITQBFJGelFWojITPz8kY+wmAAp3pWLkJVT7oGwkY3R5REar/Ln1qO7AyIfsLGxQ1eB60E3UDmi9kKWPOwmcvP69cM8kROYCXVB5FW18I4LToeLSbTGnKh2T9MIChLHBOI2lT9srMUDB9s7AM8CApj7H/yaZExsiOd9GnbngcjzOciPyVPOuW0RFB7ZvHnDck4FJgbOpwPoPRHpAdxs782bwQSwei3ty0aC2EVh61ANMutEy63Gi/l0VUfSJC7sIjAnhYYON4x7eNgI9KgeifDPIOyuuDygRWnGuKDcQF6roWrGxbSNgzDAgqBuV6ki7z9WT5y4DR0LdoqoVbSOgZhAi7hv4bXzEFarDG7yqbPmsgN2aovhdLdf+2jqBorIE4JGbIugZCvcsijQHr5ojN1tbVaustUWNNP6S0vy60AR+ut4Ndj7CYzdlgL7AixTtmRnRLOToVNAhQeDfR+yPr+iJrIbf/+pM62l0WP8RKNNCSCCTySuJjgT42MTnv4rqjEDvg6J+VH5T8/qynZSW+ts40EgCQtcQDcsDEN2d9IKe4U2hiR5jmG1ERt4QKrS4pkqK2zmRSQMaUsj6D5cuu3C5ppCY6IQLf2dn8CIjZk4Q+N961VnJvlxf+4Z6v/cojvsDYEQL4BXRHVTXfkysjqPfd3tA6YqOnXx293gnLgvV5xFBVa2iv/b6dEX91p+fbf9ImTnxX6gWAadbILARX+0rbEjxohwCFpBVlH1nyJea2KTFY+OduHUGeaFJoNN3gcwr1smu37qsze16y7pQ4a7pYFYj0iuAvho0jbTxJQBkrB6Ky70VGIDqamA9+WmnWtwrZ20/fN4uQA1rss52Slw8wIOZKg4/QIlTKBfY32DttobSZafDJS0Ka3YXYEx6UCXZid8/n6wJRwHIXrsEI7kB6+xHtQTDn8hLPQuipBb2oJOkgSQBPREuAlupq98cfa6mXsXnOFpfV5uQMBRxlMpP/kJpbmP4tNHC3SNBNiLycJAbleLlOXLGVTB33edRLUBkepAuegyRd1D7Pphh10Wr5mJuMT5fNo67L45NQSUlIBqsYlXqkqaiGy51es3eWQh5CDfmUeufTcaE9QBkFY7AyIuImXxT3bfI7doUvQayFbUDMSZYRHiLq64n2JByOXzvBzLGbkJ1GarnP4OPmBt60Or0v+E381Fdj+ILOhYT4p1ADOhTzcA3EdjeXvCtEwDIGNc0J6vNB/0FDXXbmk9uc05wMSYLq+nAgYArtWJ3keYW0eVUVheE9/3ArbYXkND+mbO2H8gk0AnAlGZAb3WlWpQ/I/o6q9JevWMxIyJdWWZ+V4xnOUaebQH4MVS2I/59eO3bFGbWdEiNiVhrmVncH+NfhMg0IA4RH1bfQnQlq9LKwiYnRbTB/9HmKLrWfg2j94HUoI1/Z3XOOe6te+vuWf8DkM0cb7DOQZgAAAAASUVORK5CYII='  # nopep8'
11
 
41
 
12
 
42
 
13
 def get_redis_connection(config: CFG) -> Redis:
43
 def get_redis_connection(config: CFG) -> Redis:
72
     # webdav utils, it may cause trouble. So, it should be replaced to
102
     # webdav utils, it may cause trouble. So, it should be replaced to
73
     # a character which will not change in bdd.
103
     # a character which will not change in bdd.
74
     return datetime.datetime.now().isoformat().replace(':', '.')
104
     return datetime.datetime.now().isoformat().replace(':', '.')
105
+
106
+# INFO - G.M - 2018-08-02 - Simple password generator, inspired by
107
+# https://gist.github.com/23maverick23/4131896
108
+
109
+
110
+ALLOWED_AUTOGEN_PASSWORD_CHAR = string.ascii_letters + \
111
+                                string.digits + \
112
+                                string.punctuation
113
+
114
+DEFAULT_PASSWORD_GEN_CHAR_LENGTH = 12
115
+
116
+
117
+def password_generator(
118
+        length: int=DEFAULT_PASSWORD_GEN_CHAR_LENGTH,
119
+        chars: str=ALLOWED_AUTOGEN_PASSWORD_CHAR
120
+) -> str:
121
+    """
122
+    :param length: length of the new password
123
+    :param chars: characters allowed
124
+    :return: password as string
125
+    """
126
+    return ''.join(random.choice(chars) for char_number in range(length))
127
+

+ 7 - 5
backend/tracim_backend/lib/webdav/dav_provider.py View File

8
 from tracim_backend import CFG
8
 from tracim_backend import CFG
9
 from tracim_backend.lib.webdav.utils import transform_to_bdd, HistoryType, \
9
 from tracim_backend.lib.webdav.utils import transform_to_bdd, HistoryType, \
10
     SpecialFolderExtension
10
     SpecialFolderExtension
11
+from tracim_backend.models.contents import CONTENT_TYPES
11
 
12
 
12
 from wsgidav.dav_provider import DAVProvider
13
 from wsgidav.dav_provider import DAVProvider
13
 from wsgidav.lock_manager import LockManager
14
 from wsgidav.lock_manager import LockManager
19
 from tracim_backend.lib.core.workspace import WorkspaceApi
20
 from tracim_backend.lib.core.workspace import WorkspaceApi
20
 from tracim_backend.lib.webdav import resources
21
 from tracim_backend.lib.webdav import resources
21
 from tracim_backend.lib.webdav.utils import normpath
22
 from tracim_backend.lib.webdav.utils import normpath
22
-from tracim_backend.models.data import ContentType, Content, Workspace
23
+from tracim_backend.models.data import Content
24
+from tracim_backend.models.data import Workspace
23
 
25
 
24
 
26
 
25
 class Provider(DAVProvider):
27
 class Provider(DAVProvider):
174
             content_revision = content_api.get_one_revision(revision_id)
176
             content_revision = content_api.get_one_revision(revision_id)
175
             content = self.get_content_from_revision(content_revision, content_api)
177
             content = self.get_content_from_revision(content_revision, content_api)
176
 
178
 
177
-            if content.type == ContentType.File:
179
+            if content.type == CONTENT_TYPES.File.slug:
178
                 return resources.HistoryFileResource(
180
                 return resources.HistoryFileResource(
179
                     path=path,
181
                     path=path,
180
                     environ=environ,
182
                     environ=environ,
198
 
200
 
199
         if content is None:
201
         if content is None:
200
             return None
202
             return None
201
-        if content.type == ContentType.Folder:
203
+        if content.type == CONTENT_TYPES.Folder.slug:
202
             return resources.FolderResource(
204
             return resources.FolderResource(
203
                 path=path,
205
                 path=path,
204
                 environ=environ,
206
                 environ=environ,
207
                 session=session,
209
                 session=session,
208
                 user=user,
210
                 user=user,
209
             )
211
             )
210
-        elif content.type == ContentType.File:
212
+        elif content.type == CONTENT_TYPES.File.slug:
211
             return resources.FileResource(
213
             return resources.FileResource(
212
                 path=path,
214
                 path=path,
213
                 environ=environ,
215
                 environ=environ,
356
 
358
 
357
     def get_content_from_revision(self, revision: ContentRevisionRO, api: ContentApi) -> Content:
359
     def get_content_from_revision(self, revision: ContentRevisionRO, api: ContentApi) -> Content:
358
         try:
360
         try:
359
-            return api.get_one(revision.content_id, ContentType.Any)
361
+            return api.get_one(revision.content_id, CONTENT_TYPES.Any_SLUG)
360
         except NoResultFound:
362
         except NoResultFound:
361
             return None
363
             return None
362
 
364
 

+ 3 - 2
backend/tracim_backend/lib/webdav/design.py View File

1
 #coding: utf8
1
 #coding: utf8
2
 from datetime import datetime
2
 from datetime import datetime
3
 
3
 
4
+from tracim_backend.models.contents import CONTENT_TYPES
4
 from tracim_backend.models.data import VirtualEvent
5
 from tracim_backend.models.data import VirtualEvent
5
-from tracim_backend.models.data import ContentType
6
 from tracim_backend.models import data
6
 from tracim_backend.models import data
7
 
7
 
8
 # FIXME: fix temporaire ...
8
 # FIXME: fix temporaire ...
237
 
237
 
238
     return page
238
     return page
239
 
239
 
240
+
240
 def designThread(content: data.Content, content_revision: data.ContentRevisionRO, comments) -> str:
241
 def designThread(content: data.Content, content_revision: data.ContentRevisionRO, comments) -> str:
241
         hist = content.get_history(drop_empty_revision=False)
242
         hist = content.get_history(drop_empty_revision=False)
242
 
243
 
248
         disc = ''
249
         disc = ''
249
         participants = {}
250
         participants = {}
250
         for t in allT:
251
         for t in allT:
251
-            if t.type == ContentType.Comment:
252
+            if t.type == CONTENT_TYPES.Comment.slug:
252
                 disc += '''
253
                 disc += '''
253
                     <div class="row comment comment-row">
254
                     <div class="row comment comment-row">
254
                         <i class="fa fa-comment-o comment-icon"></i>
255
                         <i class="fa fa-comment-o comment-icon"></i>

+ 37 - 34
backend/tracim_backend/lib/webdav/resources.py View File

19
     FakeFileStream
19
     FakeFileStream
20
 from tracim_backend.lib.webdav.utils import transform_to_bdd
20
 from tracim_backend.lib.webdav.utils import transform_to_bdd
21
 from tracim_backend.lib.core.workspace import WorkspaceApi
21
 from tracim_backend.lib.core.workspace import WorkspaceApi
22
+from tracim_backend.models.contents import CONTENT_TYPES
22
 from tracim_backend.models.data import User, ContentRevisionRO
23
 from tracim_backend.models.data import User, ContentRevisionRO
23
 from tracim_backend.models.data import Workspace
24
 from tracim_backend.models.data import Workspace
24
-from tracim_backend.models.data import Content, ActionDescription
25
-from tracim_backend.models.data import ContentType
25
+from tracim_backend.models.data import Content
26
+from tracim_backend.models.data import ActionDescription
26
 from tracim_backend.lib.webdav.design import designThread, designPage
27
 from tracim_backend.lib.webdav.design import designThread, designPage
27
 
28
 
28
 from wsgidav import compat
29
 from wsgidav import compat
236
 
237
 
237
         for content in children:
238
         for content in children:
238
             # the purpose is to display .history only if there's at least one content's type that has a history
239
             # the purpose is to display .history only if there's at least one content's type that has a history
239
-            if content.type != ContentType.Folder:
240
+            if content.type != CONTENT_TYPES.Folder.slug:
240
                 self._file_count += 1
241
                 self._file_count += 1
241
             retlist.append(content.get_label_as_file())
242
             retlist.append(content.get_label_as_file())
242
 
243
 
288
             raise DAVError(HTTP_FORBIDDEN)
289
             raise DAVError(HTTP_FORBIDDEN)
289
 
290
 
290
         folder = self.content_api.create(
291
         folder = self.content_api.create(
291
-            content_type=ContentType.Folder,
292
+            content_type_slug=CONTENT_TYPES.Folder.slug,
292
             workspace=self.workspace,
293
             workspace=self.workspace,
293
             label=label,
294
             label=label,
294
             parent=self.content
295
             parent=self.content
331
     def getMemberList(self) -> [_DAVResource]:
332
     def getMemberList(self) -> [_DAVResource]:
332
         members = []
333
         members = []
333
 
334
 
334
-        children = self.content_api.get_all(False, ContentType.Any, self.workspace)
335
+        children = self.content_api.get_all(False, CONTENT_TYPES.Any_SLUG, self.workspace)
335
 
336
 
336
         for content in children:
337
         for content in children:
337
             content_path = '%s/%s' % (self.path, transform_to_display(content.get_label_as_file()))
338
             content_path = '%s/%s' % (self.path, transform_to_display(content.get_label_as_file()))
338
 
339
 
339
-            if content.type == ContentType.Folder:
340
+            if content.type == CONTENT_TYPES.Folder.slug:
340
                 members.append(
341
                 members.append(
341
                     FolderResource(
342
                     FolderResource(
342
                         path=content_path,
343
                         path=content_path,
347
                         session=self.session,
348
                         session=self.session,
348
                     )
349
                     )
349
                 )
350
                 )
350
-            elif content.type == ContentType.File:
351
+            elif content.type == CONTENT_TYPES.File.slug:
351
                 self._file_count += 1
352
                 self._file_count += 1
352
                 members.append(
353
                 members.append(
353
                     FileResource(
354
                     FileResource(
553
         )
554
         )
554
         visible_children = content_api.get_all(
555
         visible_children = content_api.get_all(
555
             self.content.content_id,
556
             self.content.content_id,
556
-            ContentType.Any,
557
+            CONTENT_TYPES.Any_SLUG,
557
             self.workspace,
558
             self.workspace,
558
         )
559
         )
559
 
560
 
561
             content_path = '%s/%s' % (self.path, transform_to_display(content.get_label_as_file()))
562
             content_path = '%s/%s' % (self.path, transform_to_display(content.get_label_as_file()))
562
 
563
 
563
             try:
564
             try:
564
-                if content.type == ContentType.Folder:
565
+                if content.type == CONTENT_TYPES.Folder.slug:
565
                     members.append(
566
                     members.append(
566
                         FolderResource(
567
                         FolderResource(
567
                             path=content_path,
568
                             path=content_path,
572
                             session=self.session,
573
                             session=self.session,
573
                         )
574
                         )
574
                     )
575
                     )
575
-                elif content.type == ContentType.File:
576
+                elif content.type == CONTENT_TYPES.File.slug:
576
                     self._file_count += 1
577
                     self._file_count += 1
577
                     members.append(
578
                     members.append(
578
                         FileResource(
579
                         FileResource(
592
                             user=self.user,
593
                             user=self.user,
593
                             session=self.session,
594
                             session=self.session,
594
                         ))
595
                         ))
595
-            except Exception as exc:
596
-                logger.exception(
597
-                    'Unable to construct member {}'.format(
598
-                        content_path,
599
-                    ),
600
-                    exc_info=True,
601
-                )
596
+            except NotImplementedError as exc:
597
+                pass
598
+            # except Exception as exc:
599
+            #     logger.exception(
600
+            #         'Unable to construct member {}'.format(
601
+            #             content_path,
602
+            #         ),
603
+            #         exc_info=True,
604
+            #     )
602
 
605
 
603
         if self._file_count > 0 and self.provider.show_history():
606
         if self._file_count > 0 and self.provider.show_history():
604
             members.append(
607
             members.append(
708
         ret = []
711
         ret = []
709
 
712
 
710
         content_id = None if self.content is None else self.content.id
713
         content_id = None if self.content is None else self.content.id
711
-        for content in self.content_api.get_all(content_id, ContentType.Any, self.workspace):
714
+        for content in self.content_api.get_all(content_id, CONTENT_TYPES.Any_SLUG, self.workspace):
712
             if (self._is_archived and content.is_archived or
715
             if (self._is_archived and content.is_archived or
713
                 self._is_deleted and content.is_deleted or
716
                 self._is_deleted and content.is_deleted or
714
                 not (content.is_archived or self._is_archived or content.is_deleted or self._is_deleted))\
717
                 not (content.is_archived or self._is_archived or content.is_deleted or self._is_deleted))\
715
-                    and content.type != ContentType.Folder:
718
+                    and content.type != CONTENT_TYPES.Folder.slug:
716
                 ret.append(content.get_label_as_file())
719
                 ret.append(content.get_label_as_file())
717
 
720
 
718
         return ret
721
         return ret
741
         if self.content:
744
         if self.content:
742
             children = self.content.children
745
             children = self.content.children
743
         else:
746
         else:
744
-            children = self.content_api.get_all(False, ContentType.Any, self.workspace)
747
+            children = self.content_api.get_all(False, CONTENT_TYPES.Any_SLUG, self.workspace)
745
         
748
         
746
         for content in children:
749
         for content in children:
747
             if content.is_archived == self._is_archived and content.is_deleted == self._is_deleted:
750
             if content.is_archived == self._is_archived and content.is_deleted == self._is_deleted:
812
         if self.content:
815
         if self.content:
813
             children = self.content.children
816
             children = self.content.children
814
         else:
817
         else:
815
-            children = self.content_api.get_all(False, ContentType.Any, self.workspace)
818
+            children = self.content_api.get_all(False, CONTENT_TYPES.Any_SLUG, self.workspace)
816
 
819
 
817
         for content in children:
820
         for content in children:
818
             if content.is_deleted:
821
             if content.is_deleted:
819
                 retlist.append(content.get_label_as_file())
822
                 retlist.append(content.get_label_as_file())
820
 
823
 
821
-                if content.type != ContentType.Folder:
824
+                if content.type != CONTENT_TYPES.Folder.slug:
822
                     self._file_count += 1
825
                     self._file_count += 1
823
 
826
 
824
         return retlist
827
         return retlist
829
         if self.content:
832
         if self.content:
830
             children = self.content.children
833
             children = self.content.children
831
         else:
834
         else:
832
-            children = self.content_api.get_all(False, ContentType.Any, self.workspace)
835
+            children = self.content_api.get_all(False, CONTENT_TYPES.Any_SLUG, self.workspace)
833
 
836
 
834
         for content in children:
837
         for content in children:
835
             if content.is_deleted:
838
             if content.is_deleted:
836
                 content_path = '%s/%s' % (self.path, transform_to_display(content.get_label_as_file()))
839
                 content_path = '%s/%s' % (self.path, transform_to_display(content.get_label_as_file()))
837
 
840
 
838
-                if content.type == ContentType.Folder:
841
+                if content.type == CONTENT_TYPES.Folder.slug:
839
                     members.append(
842
                     members.append(
840
                         FolderResource(
843
                         FolderResource(
841
                             content_path,
844
                             content_path,
845
                             user=self.user,
848
                             user=self.user,
846
                             session=self.session,
849
                             session=self.session,
847
                         ))
850
                         ))
848
-                elif content.type == ContentType.File:
851
+                elif content.type == CONTENT_TYPES.File.slug:
849
                     self._file_count += 1
852
                     self._file_count += 1
850
                     members.append(
853
                     members.append(
851
                         FileResource(
854
                         FileResource(
936
         retlist = []
939
         retlist = []
937
 
940
 
938
         for content in self.content_api.get_all_with_filter(
941
         for content in self.content_api.get_all_with_filter(
939
-                self.content if self.content is None else self.content.id, ContentType.Any):
942
+                self.content if self.content is None else self.content.id, CONTENT_TYPES.Any_SLUG):
940
             retlist.append(content.get_label_as_file())
943
             retlist.append(content.get_label_as_file())
941
 
944
 
942
-            if content.type != ContentType.Folder:
945
+            if content.type != CONTENT_TYPES.Folder.slug:
943
                 self._file_count += 1
946
                 self._file_count += 1
944
 
947
 
945
         return retlist
948
         return retlist
950
         if self.content:
953
         if self.content:
951
             children = self.content.children
954
             children = self.content.children
952
         else:
955
         else:
953
-            children = self.content_api.get_all(False, ContentType.Any, self.workspace)
956
+            children = self.content_api.get_all(False, CONTENT_TYPES.Any_SLUG, self.workspace)
954
 
957
 
955
         for content in children:
958
         for content in children:
956
             if content.is_archived:
959
             if content.is_archived:
957
                 content_path = '%s/%s' % (self.path, transform_to_display(content.get_label_as_file()))
960
                 content_path = '%s/%s' % (self.path, transform_to_display(content.get_label_as_file()))
958
 
961
 
959
-                if content.type == ContentType.Folder:
962
+                if content.type == CONTENT_TYPES.Folder.slug:
960
                     members.append(
963
                     members.append(
961
                         FolderResource(
964
                         FolderResource(
962
                             content_path,
965
                             content_path,
966
                             user=self.user,
969
                             user=self.user,
967
                             session=self.session,
970
                             session=self.session,
968
                         ))
971
                         ))
969
-                elif content.type == ContentType.File:
972
+                elif content.type == CONTENT_TYPES.File.slug:
970
                     self._file_count += 1
973
                     self._file_count += 1
971
                     members.append(
974
                     members.append(
972
                         FileResource(
975
                         FileResource(
1053
 
1056
 
1054
         left_side = '%s/(%d - %s) ' % (self.path, revision.revision_id, revision.revision_type)
1057
         left_side = '%s/(%d - %s) ' % (self.path, revision.revision_id, revision.revision_type)
1055
 
1058
 
1056
-        if self.content.type == ContentType.File:
1059
+        if self.content.type == CONTENT_TYPES.File.slug:
1057
             return HistoryFileResource(
1060
             return HistoryFileResource(
1058
                 path='%s%s' % (left_side, transform_to_display(revision.file_name)),
1061
                 path='%s%s' % (left_side, transform_to_display(revision.file_name)),
1059
                 environ=self.environ,
1062
                 environ=self.environ,
1079
 
1082
 
1080
             left_side = '%s/(%d - %s) ' % (self.path, content.revision_id, content.revision_type)
1083
             left_side = '%s/(%d - %s) ' % (self.path, content.revision_id, content.revision_type)
1081
 
1084
 
1082
-            if self.content.type == ContentType.File:
1085
+            if self.content.type == CONTENT_TYPES.File.slug:
1083
                 members.append(HistoryFileResource(
1086
                 members.append(HistoryFileResource(
1084
                     path='%s%s' % (left_side, transform_to_display(content.file_name)),
1087
                     path='%s%s' % (left_side, transform_to_display(content.file_name)),
1085
                     environ=self.environ,
1088
                     environ=self.environ,
1425
         return filestream
1428
         return filestream
1426
 
1429
 
1427
     def design(self):
1430
     def design(self):
1428
-        if self.content.type == ContentType.Page:
1431
+        if self.content.type == CONTENT_TYPES.Page.slug:
1429
             return designPage(self.content, self.content_revision)
1432
             return designPage(self.content, self.content_revision)
1430
         else:
1433
         else:
1431
             return designThread(
1434
             return designThread(
1432
                 self.content,
1435
                 self.content,
1433
                 self.content_revision,
1436
                 self.content_revision,
1434
-                self.content_api.get_all(self.content.content_id, ContentType.Comment)
1437
+                self.content_api.get_all(self.content.content_id, CONTENT_TYPES.Comment.slug)
1435
             )
1438
             )
1436
 
1439
 
1437
 
1440
 

+ 5 - 3
backend/tracim_backend/lib/webdav/utils.py View File

4
 from os.path import normpath as base_normpath
4
 from os.path import normpath as base_normpath
5
 
5
 
6
 from sqlalchemy.orm import Session
6
 from sqlalchemy.orm import Session
7
+from tracim_backend.models.contents import CONTENT_TYPES
7
 from wsgidav import util
8
 from wsgidav import util
8
 from wsgidav import compat
9
 from wsgidav import compat
9
 
10
 
10
 from tracim_backend.lib.core.content import ContentApi
11
 from tracim_backend.lib.core.content import ContentApi
11
-from tracim_backend.models.data import Workspace, Content, ContentType, \
12
-    ActionDescription
12
+from tracim_backend.models.data import Workspace
13
+from tracim_backend.models.data import Content
14
+from tracim_backend.models.data import ActionDescription
13
 from tracim_backend.models.revision_protection import new_revision
15
 from tracim_backend.models.revision_protection import new_revision
14
 
16
 
15
 
17
 
177
 
179
 
178
         file = self._api.create(
180
         file = self._api.create(
179
             filename=self._file_name,
181
             filename=self._file_name,
180
-            content_type=ContentType.File,
182
+            content_type_slug=CONTENT_TYPES.File.slug,
181
             workspace=self._workspace,
183
             workspace=self._workspace,
182
             parent=self._parent,
184
             parent=self._parent,
183
             is_temporary=is_temporary
185
             is_temporary=is_temporary

+ 20 - 2
backend/tracim_backend/models/applications.py View File

36
         self.config = config
36
         self.config = config
37
         self.main_route = main_route
37
         self.main_route = main_route
38
 
38
 
39
+    # TODO - G.M - 2018-08-07 - Refactor slug coherence issue like this one.
40
+    # we probably should not have 2 kind of slug
41
+    @property
42
+    def minislug(self):
43
+        return self.slug.replace('contents/', '')
44
+
39
 
45
 
40
 # default apps
46
 # default apps
41
 calendar = Application(
47
 calendar = Application(
59
 
65
 
60
 )
66
 )
61
 
67
 
68
+folder = Application(
69
+    label='Folder',
70
+    slug='contents/folder',
71
+    fa_icon='folder-open-o',
72
+    hexcolor='#252525',
73
+    is_active=True,
74
+    config={},
75
+    main_route='',
76
+)
77
+
62
 _file = Application(
78
 _file = Application(
63
     label='Files',
79
     label='Files',
64
     slug='contents/file',
80
     slug='contents/file',
92
 # List of applications
108
 # List of applications
93
 applications = [
109
 applications = [
94
     html_documents,
110
     html_documents,
95
-    markdownpluspage,
111
+    # TODO - G.M - 2018-08-02 - Restore markdownpage app
112
+    # markdownpluspage,
96
     _file,
113
     _file,
97
     thread,
114
     thread,
98
-    calendar,
115
+    folder,
116
+    # calendar,
99
 ]
117
 ]

+ 113 - 146
backend/tracim_backend/models/contents.py View File

6
 from tracim_backend.exceptions import ContentStatusNotExist
6
 from tracim_backend.exceptions import ContentStatusNotExist
7
 from tracim_backend.models.applications import html_documents
7
 from tracim_backend.models.applications import html_documents
8
 from tracim_backend.models.applications import _file
8
 from tracim_backend.models.applications import _file
9
+from tracim_backend.models.applications import folder
9
 from tracim_backend.models.applications import thread
10
 from tracim_backend.models.applications import thread
10
 from tracim_backend.models.applications import markdownpluspage
11
 from tracim_backend.models.applications import markdownpluspage
11
 
12
 
19
     CLOSED = 'closed'
20
     CLOSED = 'closed'
20
 
21
 
21
 
22
 
22
-class NewContentStatus(object):
23
+class ContentStatus(object):
23
     """
24
     """
24
-    Future ContentStatus object class
25
+    ContentStatus object class
25
     """
26
     """
26
     def __init__(
27
     def __init__(
27
             self,
28
             self,
38
         self.hexcolor = hexcolor
39
         self.hexcolor = hexcolor
39
 
40
 
40
 
41
 
41
-open_status = NewContentStatus(
42
+open_status = ContentStatus(
42
     slug='open',
43
     slug='open',
43
     global_status=GlobalStatus.OPEN.value,
44
     global_status=GlobalStatus.OPEN.value,
44
     label='Open',
45
     label='Open',
46
     hexcolor='#3f52e3',
47
     hexcolor='#3f52e3',
47
 )
48
 )
48
 
49
 
49
-closed_validated_status = NewContentStatus(
50
+closed_validated_status = ContentStatus(
50
     slug='closed-validated',
51
     slug='closed-validated',
51
     global_status=GlobalStatus.CLOSED.value,
52
     global_status=GlobalStatus.CLOSED.value,
52
     label='Validated',
53
     label='Validated',
54
     hexcolor='#008000',
55
     hexcolor='#008000',
55
 )
56
 )
56
 
57
 
57
-closed_unvalidated_status = NewContentStatus(
58
+closed_unvalidated_status = ContentStatus(
58
     slug='closed-unvalidated',
59
     slug='closed-unvalidated',
59
     global_status=GlobalStatus.CLOSED.value,
60
     global_status=GlobalStatus.CLOSED.value,
60
     label='Cancelled',
61
     label='Cancelled',
62
     hexcolor='#f63434',
63
     hexcolor='#f63434',
63
 )
64
 )
64
 
65
 
65
-closed_deprecated_status = NewContentStatus(
66
+closed_deprecated_status = ContentStatus(
66
     slug='closed-deprecated',
67
     slug='closed-deprecated',
67
     global_status=GlobalStatus.CLOSED.value,
68
     global_status=GlobalStatus.CLOSED.value,
68
     label='Deprecated',
69
     label='Deprecated',
71
 )
72
 )
72
 
73
 
73
 
74
 
74
-CONTENT_DEFAULT_STATUS = [
75
-    open_status,
76
-    closed_validated_status,
77
-    closed_unvalidated_status,
78
-    closed_deprecated_status,
79
-]
75
+class ContentStatusList(object):
80
 
76
 
77
+    OPEN = open_status
81
 
78
 
82
-class ContentStatusLegacy(NewContentStatus):
83
-    """
84
-    Temporary remplacement object for Legacy ContentStatus Object
85
-    """
86
-    OPEN = open_status.slug
87
-    CLOSED_VALIDATED = closed_validated_status.slug
88
-    CLOSED_UNVALIDATED = closed_unvalidated_status.slug
89
-    CLOSED_DEPRECATED = closed_deprecated_status.slug
90
-
91
-    def __init__(self, slug: str):
92
-        for status in CONTENT_DEFAULT_STATUS:
93
-            if slug == status.slug:
94
-                super(ContentStatusLegacy, self).__init__(
95
-                    slug=status.slug,
96
-                    global_status=status.global_status,
97
-                    label=status.label,
98
-                    fa_icon=status.fa_icon,
99
-                    hexcolor=status.hexcolor,
100
-                )
101
-                return
79
+    def __init__(self, extend_content_status: typing.List[ContentStatus]):
80
+        self._content_status = [self.OPEN]
81
+        self._content_status.extend(extend_content_status)
82
+
83
+    def get_one_by_slug(self, slug: str) -> ContentStatus:
84
+        for item in self._content_status:
85
+            if item.slug == slug:
86
+                return item
102
         raise ContentStatusNotExist()
87
         raise ContentStatusNotExist()
103
 
88
 
104
-    @classmethod
105
-    def all(cls, type='') -> ['NewContentStatus']:
106
-        return CONTENT_DEFAULT_STATUS
89
+    def get_all_slugs_values(self) -> typing.List[str]:
90
+        """ Get alls slugs"""
91
+        return [item.slug for item in self._content_status]
92
+
93
+    def get_all(self) -> typing.List[ContentStatus]:
94
+        """ Get all status"""
95
+        return [item for item in self._content_status]
107
 
96
 
108
-    @classmethod
109
-    def allowed_values(cls):
110
-        return [status.slug for status in CONTENT_DEFAULT_STATUS]
97
+    def get_default_status(self) -> ContentStatus:
98
+        return self.OPEN
111
 
99
 
112
 
100
 
101
+CONTENT_STATUS = ContentStatusList(
102
+    [
103
+        closed_validated_status,
104
+        closed_unvalidated_status,
105
+        closed_deprecated_status,
106
+    ]
107
+)
113
 ####
108
 ####
114
 # ContentType
109
 # ContentType
115
 
110
 
116
 
111
 
117
-class NewContentType(object):
112
+class ContentType(object):
118
     """
113
     """
119
     Future ContentType object class
114
     Future ContentType object class
120
     """
115
     """
125
             hexcolor: str,
120
             hexcolor: str,
126
             label: str,
121
             label: str,
127
             creation_label: str,
122
             creation_label: str,
128
-            available_statuses: typing.List[NewContentStatus],
129
-
123
+            available_statuses: typing.List[ContentStatus],
124
+            slug_alias: typing.List[str] = None,
130
     ):
125
     ):
131
         self.slug = slug
126
         self.slug = slug
132
         self.fa_icon = fa_icon
127
         self.fa_icon = fa_icon
134
         self.label = label
129
         self.label = label
135
         self.creation_label = creation_label
130
         self.creation_label = creation_label
136
         self.available_statuses = available_statuses
131
         self.available_statuses = available_statuses
132
+        self.slug_alias = slug_alias
137
 
133
 
138
 
134
 
139
-thread_type = NewContentType(
135
+thread_type = ContentType(
140
     slug='thread',
136
     slug='thread',
141
     fa_icon=thread.fa_icon,
137
     fa_icon=thread.fa_icon,
142
     hexcolor=thread.hexcolor,
138
     hexcolor=thread.hexcolor,
143
     label='Thread',
139
     label='Thread',
144
     creation_label='Discuss about a topic',
140
     creation_label='Discuss about a topic',
145
-    available_statuses=CONTENT_DEFAULT_STATUS,
141
+    available_statuses=CONTENT_STATUS.get_all(),
146
 )
142
 )
147
 
143
 
148
-file_type = NewContentType(
144
+file_type = ContentType(
149
     slug='file',
145
     slug='file',
150
     fa_icon=_file.fa_icon,
146
     fa_icon=_file.fa_icon,
151
     hexcolor=_file.hexcolor,
147
     hexcolor=_file.hexcolor,
152
     label='File',
148
     label='File',
153
     creation_label='Upload a file',
149
     creation_label='Upload a file',
154
-    available_statuses=CONTENT_DEFAULT_STATUS,
150
+    available_statuses=CONTENT_STATUS.get_all(),
155
 )
151
 )
156
 
152
 
157
-markdownpluspage_type = NewContentType(
153
+markdownpluspage_type = ContentType(
158
     slug='markdownpage',
154
     slug='markdownpage',
159
     fa_icon=markdownpluspage.fa_icon,
155
     fa_icon=markdownpluspage.fa_icon,
160
     hexcolor=markdownpluspage.hexcolor,
156
     hexcolor=markdownpluspage.hexcolor,
161
     label='Rich Markdown File',
157
     label='Rich Markdown File',
162
     creation_label='Create a Markdown document',
158
     creation_label='Create a Markdown document',
163
-    available_statuses=CONTENT_DEFAULT_STATUS,
159
+    available_statuses=CONTENT_STATUS.get_all(),
164
 )
160
 )
165
 
161
 
166
-html_documents_type = NewContentType(
162
+html_documents_type = ContentType(
167
     slug='html-document',
163
     slug='html-document',
168
     fa_icon=html_documents.fa_icon,
164
     fa_icon=html_documents.fa_icon,
169
     hexcolor=html_documents.hexcolor,
165
     hexcolor=html_documents.hexcolor,
170
     label='Text Document',
166
     label='Text Document',
171
     creation_label='Write a document',
167
     creation_label='Write a document',
172
-    available_statuses=CONTENT_DEFAULT_STATUS,
168
+    available_statuses=CONTENT_STATUS.get_all(),
169
+    slug_alias=['page']
173
 )
170
 )
174
 
171
 
175
 # TODO - G.M - 31-05-2018 - Set Better folder params
172
 # TODO - G.M - 31-05-2018 - Set Better folder params
176
-folder_type = NewContentType(
173
+folder_type = ContentType(
177
     slug='folder',
174
     slug='folder',
178
-    fa_icon=thread.fa_icon,
179
-    hexcolor=thread.hexcolor,
175
+    fa_icon=folder.fa_icon,
176
+    hexcolor=folder.hexcolor,
180
     label='Folder',
177
     label='Folder',
181
-    creation_label='Create collection of any documents',
182
-    available_statuses=CONTENT_DEFAULT_STATUS,
178
+    creation_label='Create a folder',
179
+    available_statuses=CONTENT_STATUS.get_all(),
183
 )
180
 )
184
 
181
 
185
-CONTENT_DEFAULT_TYPE = [
186
-    thread_type,
187
-    file_type,
188
-    markdownpluspage_type,
189
-    html_documents_type,
190
-    folder_type,
191
-]
192
 
182
 
193
 # TODO - G.M - 31-05-2018 - Set Better Event params
183
 # TODO - G.M - 31-05-2018 - Set Better Event params
194
-event_type = NewContentType(
184
+event_type = ContentType(
195
     slug='event',
185
     slug='event',
196
     fa_icon=thread.fa_icon,
186
     fa_icon=thread.fa_icon,
197
     hexcolor=thread.hexcolor,
187
     hexcolor=thread.hexcolor,
198
     label='Event',
188
     label='Event',
199
     creation_label='Event',
189
     creation_label='Event',
200
-    available_statuses=CONTENT_DEFAULT_STATUS,
190
+    available_statuses=CONTENT_STATUS.get_all(),
201
 )
191
 )
202
 
192
 
203
 # TODO - G.M - 31-05-2018 - Set Better Event params
193
 # TODO - G.M - 31-05-2018 - Set Better Event params
204
-comment_type = NewContentType(
194
+comment_type = ContentType(
205
     slug='comment',
195
     slug='comment',
206
     fa_icon=thread.fa_icon,
196
     fa_icon=thread.fa_icon,
207
     hexcolor=thread.hexcolor,
197
     hexcolor=thread.hexcolor,
208
     label='Comment',
198
     label='Comment',
209
     creation_label='Comment',
199
     creation_label='Comment',
210
-    available_statuses=CONTENT_DEFAULT_STATUS,
200
+    available_statuses=CONTENT_STATUS.get_all(),
211
 )
201
 )
212
 
202
 
213
-CONTENT_DEFAULT_TYPE_SPECIAL = [
214
-    event_type,
215
-    comment_type,
216
-]
217
 
203
 
218
-ALL_CONTENTS_DEFAULT_TYPES = CONTENT_DEFAULT_TYPE + CONTENT_DEFAULT_TYPE_SPECIAL
219
-
220
-
221
-class ContentTypeLegacy(NewContentType):
204
+class ContentTypeList(object):
222
     """
205
     """
223
-    Temporary remplacement object for Legacy ContentType Object
206
+    ContentType List
224
     """
207
     """
225
-
226
-    # special type
227
-    Any = 'any'
228
-    Folder = 'folder'
229
-    Event = 'event'
230
-    Comment = 'comment'
231
-
232
-    File = file_type.slug
233
-    Thread = thread_type.slug
234
-    Page = html_documents_type.slug
235
-    PageLegacy = 'page'
236
-    MarkdownPage = markdownpluspage_type.slug
237
-
238
-    def __init__(self, slug: str):
239
-        if slug == 'page':
240
-            slug = ContentTypeLegacy.Page
241
-        for content_type in ALL_CONTENTS_DEFAULT_TYPES:
242
-            if slug == content_type.slug:
243
-                super(ContentTypeLegacy, self).__init__(
244
-                    slug=content_type.slug,
245
-                    fa_icon=content_type.fa_icon,
246
-                    hexcolor=content_type.hexcolor,
247
-                    label=content_type.label,
248
-                    creation_label=content_type.creation_label,
249
-                    available_statuses=content_type.available_statuses
250
-                )
251
-                return
208
+    Any_SLUG = 'any'
209
+    Folder = folder_type
210
+    Comment = comment_type
211
+    Event = event_type
212
+    File = file_type
213
+    Page = html_documents_type
214
+    Thread = thread_type
215
+
216
+    def __init__(self, extend_content_status: typing.List[ContentType]):
217
+        self._content_types = [self.Folder]
218
+        self._content_types.extend(extend_content_status)
219
+        self._special_contents_types = [self.Comment]
220
+        self._extra_slugs = [self.Any_SLUG]
221
+
222
+    def get_one_by_slug(self, slug: str) -> ContentType:
223
+        """
224
+        Get ContentType object according to slug
225
+        match for both slug and slug_alias
226
+        """
227
+        content_types = self._content_types.copy()
228
+        content_types.extend(self._special_contents_types)
229
+        for item in content_types:
230
+            if item.slug == slug or (item.slug_alias and slug in item.slug_alias):  # nopep8
231
+                return item
252
         raise ContentTypeNotExist()
232
         raise ContentTypeNotExist()
253
 
233
 
254
-    def get_slug_aliases(self) -> typing.List[str]:
234
+    def endpoint_allowed_types_slug(self) -> typing.List[str]:
255
         """
235
         """
256
-        Get all slug aliases of a content,
257
-        useful for legacy code convertion
236
+        Return restricted list of content_type:
237
+        dont return special content_type like  comment, don't return
238
+        "any" slug, dont return content type slug alias , don't return event.
239
+        Useful to restrict slug param in schema.
258
         """
240
         """
259
-        # TODO - G.M - 2018-07-05 - Remove this legacy compat code
260
-        # when possible.
261
-        page_alias = [self.Page, self.PageLegacy]
262
-        if self.slug in page_alias:
263
-            return page_alias
264
-        else:
265
-            return [self.slug]
266
-
267
-    @classmethod
268
-    def all(cls) -> typing.List[str]:
269
-        return cls.allowed_types()
270
-
271
-    @classmethod
272
-    def allowed_types(cls) -> typing.List[str]:
273
-        contents_types = [status.slug for status in ALL_CONTENTS_DEFAULT_TYPES]
274
-        return contents_types
275
-
276
-    @classmethod
277
-    def allowed_type_values(cls) -> typing.List[str]:
241
+        allowed_type_slug = [contents_type.slug for contents_type in self._content_types]  # nopep8
242
+        return allowed_type_slug
243
+
244
+    def query_allowed_types_slugs(self) -> typing.List[str]:
278
         """
245
         """
279
-        All content type slug + special values like any
246
+        Return alls allowed types slug : content_type slug + all alias, any
247
+        and special content_type like comment. Do not return event.
248
+        Usefull allowed value to perform query to database.
280
         """
249
         """
281
-        content_types = cls.allowed_types()
282
-        content_types.append(ContentTypeLegacy.Any)
283
-        return content_types
284
-
285
-    @classmethod
286
-    def allowed_types_for_folding(cls):
287
-        # This method is used for showing only "main"
288
-        # types in the left-side treeview
289
-        contents_types = [status.slug for status in CONTENT_DEFAULT_TYPE]
290
-        return contents_types
291
-
292
-    # TODO - G.M - 30-05-2018 - This method don't do anything.
293
-    @classmethod
294
-    def sorted(cls, types: ['ContentType']) -> ['ContentType']:
295
-        return types
296
-
297
-    @property
298
-    def id(self):
299
-        return self.slug
300
-
301
-    def toDict(self):
302
-        raise NotImplementedError()
250
+        allowed_types_slug = []
251
+        for content_type in self._content_types:
252
+            allowed_types_slug.append(content_type.slug)
253
+            if content_type.slug_alias:
254
+                allowed_types_slug.extend(content_type.slug_alias)
255
+        for content_type in self._special_contents_types:
256
+            allowed_types_slug.append(content_type.slug)
257
+        allowed_types_slug.extend(self._extra_slugs)
258
+        return allowed_types_slug
259
+
260
+
261
+CONTENT_TYPES = ContentTypeList(
262
+    [
263
+        thread_type,
264
+        file_type,
265
+        # TODO - G.M - 2018-08-02 - Restore markdown page content
266
+        #    markdownpluspage_type,
267
+        html_documents_type,
268
+    ]
269
+)

+ 34 - 9
backend/tracim_backend/models/context_models.py View File

5
 
5
 
6
 from slugify import slugify
6
 from slugify import slugify
7
 from sqlalchemy.orm import Session
7
 from sqlalchemy.orm import Session
8
-from tracim_backend import CFG
8
+from tracim_backend.config import CFG
9
 from tracim_backend.config import PreviewDim
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
 from tracim_backend.models import User
13
 from tracim_backend.models import User
11
 from tracim_backend.models.auth import Profile
14
 from tracim_backend.models.auth import Profile
12
 from tracim_backend.models.data import Content
15
 from tracim_backend.models.data import Content
14
 from tracim_backend.models.data import Workspace
17
 from tracim_backend.models.data import Workspace
15
 from tracim_backend.models.data import UserRoleInWorkspace
18
 from tracim_backend.models.data import UserRoleInWorkspace
16
 from tracim_backend.models.roles import WorkspaceRoles
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
 from tracim_backend.models.workspace_menu_entries import WorkspaceMenuEntry
21
 from tracim_backend.models.workspace_menu_entries import WorkspaceMenuEntry
19
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
22
+from tracim_backend.models.contents import CONTENT_TYPES
20
 
23
 
21
 
24
 
22
 class PreviewAllowedDim(object):
25
 class PreviewAllowedDim(object):
186
         self.comment_id = comment_id
189
         self.comment_id = comment_id
187
 
190
 
188
 
191
 
192
+class AutocompleteQuery(object):
193
+    """
194
+    Autocomplete query model
195
+    """
196
+    def __init__(self, acp: str):
197
+        self.acp = acp
198
+
199
+
189
 class PageQuery(object):
200
 class PageQuery(object):
190
     """
201
     """
191
     Page query model
202
     Page query model
454
         # apps)
465
         # apps)
455
         return default_workspace_menu_entry(self.workspace)
466
         return default_workspace_menu_entry(self.workspace)
456
 
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
+
457
 
476
 
458
 class UserRoleWorkspaceInContext(object):
477
 class UserRoleWorkspaceInContext(object):
459
     """
478
     """
578
 
597
 
579
     @property
598
     @property
580
     def content_type(self) -> str:
599
     def content_type(self) -> str:
581
-        content_type = ContentType(self.content.type)
600
+        content_type = CONTENT_TYPES.get_one_by_slug(self.content.type)
582
         return content_type.slug
601
         return content_type.slug
583
 
602
 
584
     @property
603
     @property
652
         assert self._user
671
         assert self._user
653
         return not self.content.has_new_information_for(self._user)
672
         return not self.content.has_new_information_for(self._user)
654
 
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
+
655
 
684
 
656
 class RevisionInContext(object):
685
 class RevisionInContext(object):
657
     """
686
     """
690
 
719
 
691
     @property
720
     @property
692
     def content_type(self) -> str:
721
     def content_type(self) -> str:
693
-        content_type = ContentType(self.revision.type)
694
-        if content_type:
695
-            return content_type.slug
696
-        else:
697
-            return None
722
+        return CONTENT_TYPES.get_one_by_slug(self.revision.type).slug
698
 
723
 
699
     @property
724
     @property
700
     def sub_content_types(self) -> typing.List[str]:
725
     def sub_content_types(self) -> typing.List[str]:

+ 26 - 28
backend/tracim_backend/models/data.py View File

96
 
96
 
97
     def get_allowed_content_types(self):
97
     def get_allowed_content_types(self):
98
         # @see Content.get_allowed_content_types()
98
         # @see Content.get_allowed_content_types()
99
-        return [ContentType('folder')]
99
+        return CONTENT_TYPES.endpoint_allowed_types_slug()
100
 
100
 
101
     def get_valid_children(
101
     def get_valid_children(
102
             self,
102
             self,
291
                 ]
291
                 ]
292
 
292
 
293
 
293
 
294
-from tracim_backend.models.contents import ContentStatusLegacy as ContentStatus
295
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
294
+from tracim_backend.models.contents import CONTENT_STATUS
295
+from tracim_backend.models.contents import ContentStatus
296
+from tracim_backend.models.contents import CONTENT_TYPES
296
 # TODO - G.M - 30-05-2018 - Drop this old code when whe are sure nothing
297
 # TODO - G.M - 30-05-2018 - Drop this old code when whe are sure nothing
297
 # is lost .
298
 # is lost .
298
 
299
 
351
 #         # self.icon = ContentStatus._ICONS[id]
352
 #         # self.icon = ContentStatus._ICONS[id]
352
 #         # self.css = ContentStatus._CSS[id]
353
 #         # self.css = ContentStatus._CSS[id]
353
 #         #
354
 #         #
354
-#         # if type==ContentType.Thread:
355
+#         # if type==CONTENT_TYPES.Thread.slug:
355
 #         #     self.label = ContentStatus._LABELS_THREAD[id]
356
 #         #     self.label = ContentStatus._LABELS_THREAD[id]
356
-#         # elif type==ContentType.File:
357
+#         # elif type==CONTENT_TYPES.File.slug:
357
 #         #     self.label = ContentStatus._LABELS_FILE[id]
358
 #         #     self.label = ContentStatus._LABELS_FILE[id]
358
 #         # else:
359
 #         # else:
359
 #         #     self.label = ContentStatus._LABELS[id]
360
 #         #     self.label = ContentStatus._LABELS[id]
499
 #     #     # TODO - DYNDATATYPE - D.A. - 2014-12-02
500
 #     #     # TODO - DYNDATATYPE - D.A. - 2014-12-02
500
 #     #     # Make this code dynamic loading data types
501
 #     #     # Make this code dynamic loading data types
501
 #     #
502
 #     #
502
-#     #     if content.type==ContentType.Folder:
503
+#     #     if content.type==CONTENT_TYPES.Folder.slug:
503
 #     #         return '/workspaces/{}/folders/{}'.format(content.workspace_id, content.content_id)
504
 #     #         return '/workspaces/{}/folders/{}'.format(content.workspace_id, content.content_id)
504
-#     #     elif content.type==ContentType.File:
505
+#     #     elif content.type==CONTENT_TYPES.File.slug:
505
 #     #         return '/workspaces/{}/folders/{}/files/{}'.format(content.workspace_id, content.parent_id, content.content_id)
506
 #     #         return '/workspaces/{}/folders/{}/files/{}'.format(content.workspace_id, content.parent_id, content.content_id)
506
-#     #     elif content.type==ContentType.Thread:
507
+#     #     elif content.type==CONTENT_TYPES.Thread.slug:
507
 #     #         return '/workspaces/{}/folders/{}/threads/{}'.format(content.workspace_id, content.parent_id, content.content_id)
508
 #     #         return '/workspaces/{}/folders/{}/threads/{}'.format(content.workspace_id, content.parent_id, content.content_id)
508
-#     #     elif content.type==ContentType.Page:
509
+#     #     elif content.type==CONTENT_TYPES.Page.slug:
509
 #     #         return '/workspaces/{}/folders/{}/pages/{}'.format(content.workspace_id, content.parent_id, content.content_id)
510
 #     #         return '/workspaces/{}/folders/{}/pages/{}'.format(content.workspace_id, content.parent_id, content.content_id)
510
 #     #
511
 #     #
511
 #     # @classmethod
512
 #     # @classmethod
544
 
545
 
545
     @classmethod
546
     @classmethod
546
     def check_properties(cls, item):
547
     def check_properties(cls, item):
547
-        if item.type == ContentType.Folder:
548
+        if item.type == CONTENT_TYPES.Folder.slug:
548
             properties = item.properties
549
             properties = item.properties
549
             if 'allowed_content' not in properties.keys():
550
             if 'allowed_content' not in properties.keys():
550
                 return False
551
                 return False
558
                 return False
559
                 return False
559
             return True
560
             return True
560
 
561
 
561
-        if item.type == ContentType.Event:
562
+        if item.type == CONTENT_TYPES.Event.slug:
562
             properties = item.properties
563
             properties = item.properties
563
             if 'name' not in properties.keys():
564
             if 'name' not in properties.keys():
564
                 return False
565
                 return False
572
 
573
 
573
         # TODO - G.M - 15-03-2018 - Choose only correct Content-type for origin
574
         # TODO - G.M - 15-03-2018 - Choose only correct Content-type for origin
574
         # Only content who can be copied need this
575
         # Only content who can be copied need this
575
-        if item.type == ContentType.Any:
576
+        if item.type == CONTENT_TYPES.Any_SLUG:
576
             properties = item.properties
577
             properties = item.properties
577
             if 'origin' in properties.keys():
578
             if 'origin' in properties.keys():
578
                 return True
579
                 return True
580
 
581
 
581
     @classmethod
582
     @classmethod
582
     def reset_properties(cls, item):
583
     def reset_properties(cls, item):
583
-        if item.type == ContentType.Folder:
584
+        if item.type == CONTENT_TYPES.Folder.slug:
584
             item.properties = DEFAULT_PROPERTIES
585
             item.properties = DEFAULT_PROPERTIES
585
             return
586
             return
586
 
587
 
616
     properties = Column('properties', Text(), unique=False, nullable=False, default='')
617
     properties = Column('properties', Text(), unique=False, nullable=False, default='')
617
 
618
 
618
     type = Column(Unicode(32), unique=False, nullable=False)
619
     type = Column(Unicode(32), unique=False, nullable=False)
619
-    status = Column(Unicode(32), unique=False, nullable=False, default=ContentStatus.OPEN)
620
+    status = Column(Unicode(32), unique=False, nullable=False, default=str(CONTENT_STATUS.get_default_status().slug))
620
     created = Column(DateTime, unique=False, nullable=False, default=datetime.utcnow)
621
     created = Column(DateTime, unique=False, nullable=False, default=datetime.utcnow)
621
     updated = Column(DateTime, unique=False, nullable=False, default=datetime.utcnow)
622
     updated = Column(DateTime, unique=False, nullable=False, default=datetime.utcnow)
622
     is_deleted = Column(Boolean, unique=False, nullable=False, default=False)
623
     is_deleted = Column(Boolean, unique=False, nullable=False, default=False)
754
         super().__setattr__(key, value)
755
         super().__setattr__(key, value)
755
 
756
 
756
     def get_status(self) -> ContentStatus:
757
     def get_status(self) -> ContentStatus:
757
-        return ContentStatus(self.status)
758
+        return CONTENT_STATUS.get_one_by_slug(self.status)
758
 
759
 
759
     def get_label(self) -> str:
760
     def get_label(self) -> str:
760
         return self.label or self.file_name or ''
761
         return self.label or self.file_name or ''
779
     def get_label_as_file(self):
780
     def get_label_as_file(self):
780
         file_extension = self.file_extension or ''
781
         file_extension = self.file_extension or ''
781
 
782
 
782
-        if self.type == ContentType.Thread:
783
+        if self.type == CONTENT_TYPES.Thread.slug:
783
             file_extension = '.html'
784
             file_extension = '.html'
784
-        elif self.type == ContentType.Page:
785
+        elif self.type == CONTENT_TYPES.Page.slug:
785
             file_extension = '.html'
786
             file_extension = '.html'
786
 
787
 
787
         return '{0}{1}'.format(
788
         return '{0}{1}'.format(
1227
         return format_timedelta(delta_from_datetime - datetime_object,
1228
         return format_timedelta(delta_from_datetime - datetime_object,
1228
                                 locale=get_locale())
1229
                                 locale=get_locale())
1229
 
1230
 
1230
-    def get_child_nb(self, content_type: ContentType, content_status = ''):
1231
+    def get_child_nb(self, content_type: str, content_status = ''):
1231
         child_nb = 0
1232
         child_nb = 0
1232
         for child in self.get_valid_children():
1233
         for child in self.get_valid_children():
1233
-            if child.type == content_type or content_type == ContentType.Any:
1234
+            if child.type == content_type or content_type.slug == CONTENT_TYPES.Any_SLUG:
1234
                 if not content_status:
1235
                 if not content_status:
1235
                     child_nb = child_nb+1
1236
                     child_nb = child_nb+1
1236
                 elif content_status==child.status:
1237
                 elif content_status==child.status:
1247
         return self.revision.get_label_as_file()
1248
         return self.revision.get_label_as_file()
1248
 
1249
 
1249
     def get_status(self) -> ContentStatus:
1250
     def get_status(self) -> ContentStatus:
1250
-        return ContentStatus(
1251
+        return CONTENT_STATUS.get_one_by_slug(
1251
             self.status,
1252
             self.status,
1252
-            # TODO - G.M - 10-04-2018 - [Cleanup] Drop this
1253
-            # self.type.__str__()
1254
         )
1253
         )
1255
 
1254
 
1256
     def get_last_action(self) -> ActionDescription:
1255
     def get_last_action(self) -> ActionDescription:
1307
     def get_comments(self):
1306
     def get_comments(self):
1308
         children = []
1307
         children = []
1309
         for child in self.children:
1308
         for child in self.children:
1310
-            if ContentType.Comment==child.type and not child.is_deleted and not child.is_archived:
1309
+            if CONTENT_TYPES.Comment.slug == child.type and not child.is_deleted and not child.is_archived:
1311
                 children.append(child.node)
1310
                 children.append(child.node)
1312
         return children
1311
         return children
1313
 
1312
 
1348
             allowed_types = self.properties['allowed_content']
1347
             allowed_types = self.properties['allowed_content']
1349
             for type_label, is_allowed in allowed_types.items():
1348
             for type_label, is_allowed in allowed_types.items():
1350
                 if is_allowed:
1349
                 if is_allowed:
1351
-                    types.append(ContentType(type_label))
1350
+                    types.append(CONTENT_TYPES.get_one_by_slug(type_label))
1352
         except Exception as e:
1351
         except Exception as e:
1353
             print(e.__str__())
1352
             print(e.__str__())
1354
             print('----- /*\ *****')
1353
             print('----- /*\ *****')
1355
             raise ValueError('Not allowed content property')
1354
             raise ValueError('Not allowed content property')
1356
 
1355
 
1357
-        return ContentType.sorted(types)
1356
+        return types
1358
 
1357
 
1359
     def get_history(self, drop_empty_revision=False) -> '[VirtualEvent]':
1358
     def get_history(self, drop_empty_revision=False) -> '[VirtualEvent]':
1360
         events = []
1359
         events = []
1447
 
1446
 
1448
     @classmethod
1447
     @classmethod
1449
     def create_from_content(cls, content: Content):
1448
     def create_from_content(cls, content: Content):
1450
-        content_type = ContentType(content.type)
1451
 
1449
 
1452
         label = content.get_label()
1450
         label = content.get_label()
1453
-        if content.type == ContentType.Comment:
1451
+        if content.type == CONTENT_TYPES.Comment.slug:
1454
             # TODO - G.M  - 10-04-2018 - [Cleanup] Remove label param
1452
             # TODO - G.M  - 10-04-2018 - [Cleanup] Remove label param
1455
             # from this object ?
1453
             # from this object ?
1456
             label = l_('<strong>{}</strong> wrote:').format(content.owner.get_display_name())
1454
             label = l_('<strong>{}</strong> wrote:').format(content.owner.get_display_name())
1458
         return VirtualEvent(id=content.content_id,
1456
         return VirtualEvent(id=content.content_id,
1459
                             created=content.created,
1457
                             created=content.created,
1460
                             owner=content.owner,
1458
                             owner=content.owner,
1461
-                            type=content_type,
1459
+                            type=ActionDescription(content.revision_type),
1462
                             label=label,
1460
                             label=label,
1463
                             content=content.description,
1461
                             content=content.description,
1464
                             ref_object=content)
1462
                             ref_object=content)

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

45
             ${main_title}
45
             ${main_title}
46
             &mdash;&nbsp;<span style="font-weight: bold; color: #999; font-weight: bold;">
46
             &mdash;&nbsp;<span style="font-weight: bold; color: #999; font-weight: bold;">
47
               ${status_label|n}
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
             </span>
49
             </span>
50
         </td>
50
         </td>
51
       </tr>
51
       </tr>
55
     <div id="content-body">
55
     <div id="content-body">
56
         <div>${content_text|n}</div>
56
         <div>${content_text|n}</div>
57
         <div href='' id="call-to-action-container">
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
         </div>
60
         </div>
59
     </div>
61
     </div>
60
     
62
     

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

68
         </div>
68
         </div>
69
         <div id="call-to-action-container">
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
                 website_title=config.WEBSITE_TITLE
72
                 website_title=config.WEBSITE_TITLE
73
             ))}
73
             ))}
74
-
75
             <span style="">
74
             <span style="">
76
                 <a href="${login_url}" id='call-to-action-button'>${login_url}</a>
75
                 <a href="${login_url}" id='call-to-action-button'>${login_url}</a>
77
             </span>
76
             </span>

+ 11 - 14
backend/tracim_backend/tests/__init__.py View File

14
 from tracim_backend.models import DeclarativeBase
14
 from tracim_backend.models import DeclarativeBase
15
 from tracim_backend.models import get_session_factory
15
 from tracim_backend.models import get_session_factory
16
 from tracim_backend.models import get_tm_session
16
 from tracim_backend.models import get_tm_session
17
+from tracim_backend.models.contents import CONTENT_TYPES
17
 from tracim_backend.models.data import Workspace
18
 from tracim_backend.models.data import Workspace
18
-from tracim_backend.models.data import ContentType
19
 from tracim_backend.models.data import ContentRevisionRO
19
 from tracim_backend.models.data import ContentRevisionRO
20
 from tracim_backend.models.data import Content
20
 from tracim_backend.models.data import Content
21
 from tracim_backend.lib.utils.logger import logger
21
 from tracim_backend.lib.utils.logger import logger
68
 class FunctionalTest(unittest.TestCase):
68
 class FunctionalTest(unittest.TestCase):
69
 
69
 
70
     fixtures = [BaseFixture]
70
     fixtures = [BaseFixture]
71
-    sqlalchemy_url = 'sqlite:///tracim_test.sqlite'
71
+    config_uri = 'tests_configs.ini'
72
+    config_section = 'functional_test'
72
 
73
 
73
     def setUp(self):
74
     def setUp(self):
74
         logger._logger.setLevel('WARNING')
75
         logger._logger.setLevel('WARNING')
76
+
75
         DepotManager._clear()
77
         DepotManager._clear()
76
-        self.settings = {
77
-            'sqlalchemy.url': self.sqlalchemy_url,
78
-            'user.auth_token.validity': '604800',
79
-            'depot_storage_dir': '/tmp/test/depot',
80
-            'depot_storage_name': 'test',
81
-            'preview_cache_dir': '/tmp/test/preview_cache',
82
-            'preview.jpg.restricted_dims': True,
83
-            'email.notification.activated': 'false',
84
-        }
78
+        self.settings = plaster.get_settings(
79
+            self.config_uri,
80
+            self.config_section
81
+        )
85
         hapic.reset_context()
82
         hapic.reset_context()
86
         self.engine = get_engine(self.settings)
83
         self.engine = get_engine(self.settings)
87
         DeclarativeBase.metadata.create_all(self.engine)
84
         DeclarativeBase.metadata.create_all(self.engine)
127
 
124
 
128
 
125
 
129
 class FunctionalTestNoDB(FunctionalTest):
126
 class FunctionalTestNoDB(FunctionalTest):
130
-    sqlalchemy_url = 'sqlite://'
127
+    config_section = 'functional_test_no_db'
131
 
128
 
132
     def init_database(self, settings):
129
     def init_database(self, settings):
133
         self.engine = get_engine(settings)
130
         self.engine = get_engine(settings)
268
         workspace = self._create_workspace_and_test(workspace_name, user)
265
         workspace = self._create_workspace_and_test(workspace_name, user)
269
         folder = self._create_content_and_test(
266
         folder = self._create_content_and_test(
270
             folder_name, workspace,
267
             folder_name, workspace,
271
-            type=ContentType.Folder,
268
+            type=CONTENT_TYPES.Folder.slug,
272
             owner=user
269
             owner=user
273
         )
270
         )
274
         thread = self._create_content_and_test(
271
         thread = self._create_content_and_test(
275
             thread_name,
272
             thread_name,
276
             workspace,
273
             workspace,
277
-            type=ContentType.Thread,
274
+            type=CONTENT_TYPES.Thread.slug,
278
             parent=folder,
275
             parent=folder,
279
             owner=user
276
             owner=user
280
         )
277
         )

+ 85 - 38
backend/tracim_backend/tests/functional/test_contents.py View File

5
 from tracim_backend.lib.core.content import ContentApi
5
 from tracim_backend.lib.core.content import ContentApi
6
 from tracim_backend.lib.core.workspace import WorkspaceApi
6
 from tracim_backend.lib.core.workspace import WorkspaceApi
7
 from tracim_backend.models import get_tm_session
7
 from tracim_backend.models import get_tm_session
8
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
8
+from tracim_backend.models.contents import CONTENT_TYPES
9
 from tracim_backend.models.revision_protection import new_revision
9
 from tracim_backend.models.revision_protection import new_revision
10
 import io
10
 import io
11
 
11
 
16
 from tracim_backend import models
16
 from tracim_backend import models
17
 from tracim_backend.lib.core.content import ContentApi
17
 from tracim_backend.lib.core.content import ContentApi
18
 from tracim_backend.lib.core.workspace import WorkspaceApi
18
 from tracim_backend.lib.core.workspace import WorkspaceApi
19
-from tracim_backend.models.data import ContentType
20
 from tracim_backend.models import get_tm_session
19
 from tracim_backend.models import get_tm_session
21
 from tracim_backend.models.revision_protection import new_revision
20
 from tracim_backend.models.revision_protection import new_revision
22
 from tracim_backend.tests import FunctionalTest
21
 from tracim_backend.tests import FunctionalTest
117
         assert content['last_modifier']['avatar_url'] is None
116
         assert content['last_modifier']['avatar_url'] is None
118
         assert content['raw_content'] == '<p>To cook a great Tiramisu, you need many ingredients.</p>'  # nopep8
117
         assert content['raw_content'] == '<p>To cook a great Tiramisu, you need many ingredients.</p>'  # nopep8
119
 
118
 
119
+    def test_api__get_html_document__ok_200__archived_content(self) -> None:
120
+        """
121
+        Get one html document of a content
122
+        """
123
+        self.testapp.authorization = (
124
+            'Basic',
125
+            (
126
+                'admin@admin.admin',
127
+                'admin@admin.admin'
128
+            )
129
+        )
130
+        res = self.testapp.put_json(
131
+            '/api/v2/workspaces/2/contents/6/archive',
132
+            status=204
133
+        )
134
+        res = self.testapp.get(
135
+            '/api/v2/workspaces/2/html-documents/6',
136
+            status=200
137
+        )
138
+        content = res.json_body
139
+        assert content['content_type'] == 'html-document'
140
+        assert content['content_id'] == 6
141
+        assert content['is_archived'] is True
142
+
143
+    def test_api__get_html_document__ok_200__deleted_content(self) -> None:
144
+        """
145
+        Get one html document of a content
146
+        """
147
+        self.testapp.authorization = (
148
+            'Basic',
149
+            (
150
+                'admin@admin.admin',
151
+                'admin@admin.admin'
152
+            )
153
+        )
154
+        res = self.testapp.put_json(
155
+            '/api/v2/workspaces/2/contents/6/delete',
156
+            status=204
157
+        )
158
+        res = self.testapp.get(
159
+            '/api/v2/workspaces/2/html-documents/6',
160
+            status=200
161
+        )
162
+        content = res.json_body
163
+        assert content['content_type'] == 'html-document'
164
+        assert content['content_id'] == 6
165
+        assert content['is_deleted'] is True
166
+
120
     def test_api__get_html_document__err_400__wrong_content_type(self) -> None:
167
     def test_api__get_html_document__err_400__wrong_content_type(self) -> None:
121
         """
168
         """
122
         Get one html document of a content content 7 is not html_document
169
         Get one html document of a content content 7 is not html_document
480
             config=self.app_config
527
             config=self.app_config
481
         )
528
         )
482
         business_workspace = workspace_api.get_one(1)
529
         business_workspace = workspace_api.get_one(1)
483
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
530
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
484
         test_file = content_api.create(
531
         test_file = content_api.create(
485
-            content_type=ContentType.File,
532
+            content_type_slug=CONTENT_TYPES.File.slug,
486
             workspace=business_workspace,
533
             workspace=business_workspace,
487
             parent=tool_folder,
534
             parent=tool_folder,
488
             label='Test file',
535
             label='Test file',
648
             config=self.app_config
695
             config=self.app_config
649
         )
696
         )
650
         business_workspace = workspace_api.get_one(1)
697
         business_workspace = workspace_api.get_one(1)
651
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
698
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
652
         test_file = content_api.create(
699
         test_file = content_api.create(
653
-            content_type=ContentType.File,
700
+            content_type_slug=CONTENT_TYPES.File.slug,
654
             workspace=business_workspace,
701
             workspace=business_workspace,
655
             parent=tool_folder,
702
             parent=tool_folder,
656
             label='Test file',
703
             label='Test file',
703
             config=self.app_config
750
             config=self.app_config
704
         )
751
         )
705
         business_workspace = workspace_api.get_one(1)
752
         business_workspace = workspace_api.get_one(1)
706
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
753
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
707
         test_file = content_api.create(
754
         test_file = content_api.create(
708
-            content_type=ContentType.File,
755
+            content_type_slug=CONTENT_TYPES.File.slug,
709
             workspace=business_workspace,
756
             workspace=business_workspace,
710
             parent=tool_folder,
757
             parent=tool_folder,
711
             label='Test file',
758
             label='Test file',
809
             config=self.app_config
856
             config=self.app_config
810
         )
857
         )
811
         business_workspace = workspace_api.get_one(1)
858
         business_workspace = workspace_api.get_one(1)
812
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
859
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
813
         test_file = content_api.create(
860
         test_file = content_api.create(
814
-            content_type=ContentType.File,
861
+            content_type_slug=CONTENT_TYPES.File.slug,
815
             workspace=business_workspace,
862
             workspace=business_workspace,
816
             parent=tool_folder,
863
             parent=tool_folder,
817
             label='Test file',
864
             label='Test file',
882
             config=self.app_config
929
             config=self.app_config
883
         )
930
         )
884
         business_workspace = workspace_api.get_one(1)
931
         business_workspace = workspace_api.get_one(1)
885
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
932
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
886
         test_file = content_api.create(
933
         test_file = content_api.create(
887
-            content_type=ContentType.File,
934
+            content_type_slug=CONTENT_TYPES.File.slug,
888
             workspace=business_workspace,
935
             workspace=business_workspace,
889
             parent=tool_folder,
936
             parent=tool_folder,
890
             label='Test file',
937
             label='Test file',
978
             config=self.app_config
1025
             config=self.app_config
979
         )
1026
         )
980
         business_workspace = workspace_api.get_one(1)
1027
         business_workspace = workspace_api.get_one(1)
981
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1028
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
982
         test_file = content_api.create(
1029
         test_file = content_api.create(
983
-            content_type=ContentType.File,
1030
+            content_type_slug=CONTENT_TYPES.File.slug,
984
             workspace=business_workspace,
1031
             workspace=business_workspace,
985
             parent=tool_folder,
1032
             parent=tool_folder,
986
             label='Test file',
1033
             label='Test file',
1031
             config=self.app_config
1078
             config=self.app_config
1032
         )
1079
         )
1033
         business_workspace = workspace_api.get_one(1)
1080
         business_workspace = workspace_api.get_one(1)
1034
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1081
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1035
         test_file = content_api.create(
1082
         test_file = content_api.create(
1036
-            content_type=ContentType.File,
1083
+            content_type_slug=CONTENT_TYPES.File.slug,
1037
             workspace=business_workspace,
1084
             workspace=business_workspace,
1038
             parent=tool_folder,
1085
             parent=tool_folder,
1039
             label='Test file',
1086
             label='Test file',
1082
             config=self.app_config
1129
             config=self.app_config
1083
         )
1130
         )
1084
         business_workspace = workspace_api.get_one(1)
1131
         business_workspace = workspace_api.get_one(1)
1085
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1132
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1086
         test_file = content_api.create(
1133
         test_file = content_api.create(
1087
-            content_type=ContentType.File,
1134
+            content_type_slug=CONTENT_TYPES.File.slug,
1088
             workspace=business_workspace,
1135
             workspace=business_workspace,
1089
             parent=tool_folder,
1136
             parent=tool_folder,
1090
             label='Test file',
1137
             label='Test file',
1137
             config=self.app_config
1184
             config=self.app_config
1138
         )
1185
         )
1139
         business_workspace = workspace_api.get_one(1)
1186
         business_workspace = workspace_api.get_one(1)
1140
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1187
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1141
         test_file = content_api.create(
1188
         test_file = content_api.create(
1142
-            content_type=ContentType.File,
1189
+            content_type_slug=CONTENT_TYPES.File.slug,
1143
             workspace=business_workspace,
1190
             workspace=business_workspace,
1144
             parent=tool_folder,
1191
             parent=tool_folder,
1145
             label='Test file',
1192
             label='Test file',
1196
             config=self.app_config
1243
             config=self.app_config
1197
         )
1244
         )
1198
         business_workspace = workspace_api.get_one(1)
1245
         business_workspace = workspace_api.get_one(1)
1199
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1246
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1200
         test_file = content_api.create(
1247
         test_file = content_api.create(
1201
-            content_type=ContentType.File,
1248
+            content_type_slug=CONTENT_TYPES.File.slug,
1202
             workspace=business_workspace,
1249
             workspace=business_workspace,
1203
             parent=tool_folder,
1250
             parent=tool_folder,
1204
             label='Test file',
1251
             label='Test file',
1251
             config=self.app_config
1298
             config=self.app_config
1252
         )
1299
         )
1253
         business_workspace = workspace_api.get_one(1)
1300
         business_workspace = workspace_api.get_one(1)
1254
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1301
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1255
         test_file = content_api.create(
1302
         test_file = content_api.create(
1256
-            content_type=ContentType.File,
1303
+            content_type_slug=CONTENT_TYPES.File.slug,
1257
             workspace=business_workspace,
1304
             workspace=business_workspace,
1258
             parent=tool_folder,
1305
             parent=tool_folder,
1259
             label='Test file',
1306
             label='Test file',
1302
             config=self.app_config
1349
             config=self.app_config
1303
         )
1350
         )
1304
         business_workspace = workspace_api.get_one(1)
1351
         business_workspace = workspace_api.get_one(1)
1305
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1352
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1306
         test_file = content_api.create(
1353
         test_file = content_api.create(
1307
-            content_type=ContentType.File,
1354
+            content_type_slug=CONTENT_TYPES.File.slug,
1308
             workspace=business_workspace,
1355
             workspace=business_workspace,
1309
             parent=tool_folder,
1356
             parent=tool_folder,
1310
             label='Test file',
1357
             label='Test file',
1375
             config=self.app_config
1422
             config=self.app_config
1376
         )
1423
         )
1377
         business_workspace = workspace_api.get_one(1)
1424
         business_workspace = workspace_api.get_one(1)
1378
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1425
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1379
         test_file = content_api.create(
1426
         test_file = content_api.create(
1380
-            content_type=ContentType.File,
1427
+            content_type_slug=CONTENT_TYPES.File.slug,
1381
             workspace=business_workspace,
1428
             workspace=business_workspace,
1382
             parent=tool_folder,
1429
             parent=tool_folder,
1383
             label='Test file',
1430
             label='Test file',
1438
             config=self.app_config
1485
             config=self.app_config
1439
         )
1486
         )
1440
         business_workspace = workspace_api.get_one(1)
1487
         business_workspace = workspace_api.get_one(1)
1441
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1488
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1442
         test_file = content_api.create(
1489
         test_file = content_api.create(
1443
-            content_type=ContentType.File,
1490
+            content_type_slug=CONTENT_TYPES.File.slug,
1444
             workspace=business_workspace,
1491
             workspace=business_workspace,
1445
             parent=tool_folder,
1492
             parent=tool_folder,
1446
             label='Test file',
1493
             label='Test file',
1489
             config=self.app_config
1536
             config=self.app_config
1490
         )
1537
         )
1491
         business_workspace = workspace_api.get_one(1)
1538
         business_workspace = workspace_api.get_one(1)
1492
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1539
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1493
         test_file = content_api.create(
1540
         test_file = content_api.create(
1494
-            content_type=ContentType.File,
1541
+            content_type_slug=CONTENT_TYPES.File.slug,
1495
             workspace=business_workspace,
1542
             workspace=business_workspace,
1496
             parent=tool_folder,
1543
             parent=tool_folder,
1497
             label='Test file',
1544
             label='Test file',
1554
             config=self.app_config
1601
             config=self.app_config
1555
         )
1602
         )
1556
         business_workspace = workspace_api.get_one(1)
1603
         business_workspace = workspace_api.get_one(1)
1557
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1604
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1558
         test_file = content_api.create(
1605
         test_file = content_api.create(
1559
-            content_type=ContentType.File,
1606
+            content_type_slug=CONTENT_TYPES.File.slug,
1560
             workspace=business_workspace,
1607
             workspace=business_workspace,
1561
             parent=tool_folder,
1608
             parent=tool_folder,
1562
             label='Test file',
1609
             label='Test file',
1618
             config=self.app_config
1665
             config=self.app_config
1619
         )
1666
         )
1620
         business_workspace = workspace_api.get_one(1)
1667
         business_workspace = workspace_api.get_one(1)
1621
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1668
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1622
         test_file = content_api.create(
1669
         test_file = content_api.create(
1623
-            content_type=ContentType.File,
1670
+            content_type_slug=CONTENT_TYPES.File.slug,
1624
             workspace=business_workspace,
1671
             workspace=business_workspace,
1625
             parent=tool_folder,
1672
             parent=tool_folder,
1626
             label='Test file',
1673
             label='Test file',
1989
             session=dbsession,
2036
             session=dbsession,
1990
             config=self.app_config
2037
             config=self.app_config
1991
         )
2038
         )
1992
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
2039
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1993
         test_thread = content_api.create(
2040
         test_thread = content_api.create(
1994
-            content_type=ContentType.Thread,
2041
+            content_type_slug=CONTENT_TYPES.Thread.slug,
1995
             workspace=business_workspace,
2042
             workspace=business_workspace,
1996
             parent=tool_folder,
2043
             parent=tool_folder,
1997
             label='Test Thread',
2044
             label='Test Thread',

+ 5 - 5
backend/tracim_backend/tests/functional/test_mail_notification.py View File

11
 from tracim_backend.fixtures.content import Content as ContentFixture
11
 from tracim_backend.fixtures.content import Content as ContentFixture
12
 from tracim_backend.lib.utils.utils import get_redis_connection
12
 from tracim_backend.lib.utils.utils import get_redis_connection
13
 from tracim_backend.lib.utils.utils import get_rq_queue
13
 from tracim_backend.lib.utils.utils import get_rq_queue
14
-from tracim_backend.models.data import ContentType
14
+from tracim_backend.models.contents import CONTENT_TYPES
15
 
15
 
16
 from tracim_backend.lib.core.content import ContentApi
16
 from tracim_backend.lib.core.content import ContentApi
17
 from tracim_backend.lib.core.user import UserApi
17
 from tracim_backend.lib.core.user import UserApi
139
             config=self.app_config,
139
             config=self.app_config,
140
         )
140
         )
141
         item = api.create(
141
         item = api.create(
142
-            ContentType.Folder,
142
+            CONTENT_TYPES.Folder.slug,
143
             workspace,
143
             workspace,
144
             None,
144
             None,
145
             'parent',
145
             'parent',
147
             do_notify=False,
147
             do_notify=False,
148
         )
148
         )
149
         item2 = api.create(
149
         item2 = api.create(
150
-            ContentType.File,
150
+            CONTENT_TYPES.File.slug,
151
             workspace,
151
             workspace,
152
             item,
152
             item,
153
             'file1',
153
             'file1',
231
             config=self.app_config,
231
             config=self.app_config,
232
         )
232
         )
233
         item = api.create(
233
         item = api.create(
234
-            ContentType.Folder,
234
+            CONTENT_TYPES.Folder.slug,
235
             workspace,
235
             workspace,
236
             None,
236
             None,
237
             'parent',
237
             'parent',
239
             do_notify=False,
239
             do_notify=False,
240
         )
240
         )
241
         item2 = api.create(
241
         item2 = api.create(
242
-            ContentType.File,
242
+            CONTENT_TYPES.File.slug,
243
             workspace,
243
             workspace,
244
             item,
244
             item,
245
             'file1',
245
             'file1',

+ 25 - 72
backend/tracim_backend/tests/functional/test_system.py View File

1
 # coding=utf-8
1
 # coding=utf-8
2
+from tracim_backend.models.contents import CONTENT_TYPES
2
 from tracim_backend.tests import FunctionalTest
3
 from tracim_backend.tests import FunctionalTest
4
+from tracim_backend.models.applications import applications
3
 
5
 
4
 """
6
 """
5
 Tests for /api/v2/system subpath endpoints.
7
 Tests for /api/v2/system subpath endpoints.
6
 """
8
 """
7
 
9
 
10
+
8
 class TestApplicationEndpoint(FunctionalTest):
11
 class TestApplicationEndpoint(FunctionalTest):
9
     """
12
     """
10
     Tests for /api/v2/system/applications
13
     Tests for /api/v2/system/applications
23
         )
26
         )
24
         res = self.testapp.get('/api/v2/system/applications', status=200)
27
         res = self.testapp.get('/api/v2/system/applications', status=200)
25
         res = res.json_body
28
         res = res.json_body
26
-        application = res[0]
27
-        assert application['label'] == "Text Documents"
28
-        assert application['slug'] == 'contents/html-document'
29
-        assert application['fa_icon'] == 'file-text-o'
30
-        assert application['hexcolor'] == '#3f52e3'
31
-        assert application['is_active'] is True
32
-        assert 'config' in application
33
-        application = res[1]
34
-        assert application['label'] == "Markdown Plus Documents"
35
-        assert application['slug'] == 'contents/markdownpluspage'
36
-        assert application['fa_icon'] == 'file-code-o'
37
-        assert application['hexcolor'] == '#f12d2d'
38
-        assert application['is_active'] is True
39
-        assert 'config' in application
40
-        application = res[2]
41
-        assert application['label'] == "Files"
42
-        assert application['slug'] == 'contents/file'
43
-        assert application['fa_icon'] == 'paperclip'
44
-        assert application['hexcolor'] == '#FF9900'
45
-        assert application['is_active'] is True
46
-        assert 'config' in application
47
-        application = res[3]
48
-        assert application['label'] == "Threads"
49
-        assert application['slug'] == 'contents/thread'
50
-        assert application['fa_icon'] == 'comments-o'
51
-        assert application['hexcolor'] == '#ad4cf9'
52
-        assert application['is_active'] is True
53
-        assert 'config' in application
54
-        application = res[4]
55
-        assert application['label'] == "Calendar"
56
-        assert application['slug'] == 'calendar'
57
-        assert application['fa_icon'] == 'calendar'
58
-        assert application['hexcolor'] == '#757575'
59
-        assert application['is_active'] is True
60
-        assert 'config' in application
29
+        assert len(res) == len(applications)
30
+        for counter, application in enumerate(applications):
31
+            assert res[counter]['label'] == application.label
32
+            assert res[counter]['slug'] == application.slug
33
+            assert res[counter]['fa_icon'] == application.fa_icon
34
+            assert res[counter]['hexcolor'] == application.hexcolor
35
+            assert res[counter]['is_active'] == application.is_active
36
+            assert res[counter]['config'] == application.config
61
 
37
 
62
     def test_api__get_applications__err_401__unregistered_user(self):
38
     def test_api__get_applications__err_401__unregistered_user(self):
63
         """
39
         """
95
         )
71
         )
96
         res = self.testapp.get('/api/v2/system/content_types', status=200)
72
         res = self.testapp.get('/api/v2/system/content_types', status=200)
97
         res = res.json_body
73
         res = res.json_body
74
+        assert len(res) == len(CONTENT_TYPES.endpoint_allowed_types_slug())
75
+        content_types = CONTENT_TYPES.endpoint_allowed_types_slug()
98
 
76
 
99
-        content_type = res[0]
100
-        assert content_type['slug'] == 'thread'
101
-        assert content_type['fa_icon'] == 'comments-o'
102
-        assert content_type['hexcolor'] == '#ad4cf9'
103
-        assert content_type['label'] == 'Thread'
104
-        assert content_type['creation_label'] == 'Discuss about a topic'
105
-        assert 'available_statuses' in content_type
106
-        assert len(content_type['available_statuses']) == 4
107
-
108
-        content_type = res[1]
109
-        assert content_type['slug'] == 'file'
110
-        assert content_type['fa_icon'] == 'paperclip'
111
-        assert content_type['hexcolor'] == '#FF9900'
112
-        assert content_type['label'] == 'File'
113
-        assert content_type['creation_label'] == 'Upload a file'
114
-        assert 'available_statuses' in content_type
115
-        assert len(content_type['available_statuses']) == 4
116
-
117
-        content_type = res[2]
118
-        assert content_type['slug'] == 'markdownpage'
119
-        assert content_type['fa_icon'] == 'file-code-o'
120
-        assert content_type['hexcolor'] == '#f12d2d'
121
-        assert content_type['label'] == 'Rich Markdown File'
122
-        assert content_type['creation_label'] == 'Create a Markdown document'
123
-        assert 'available_statuses' in content_type
124
-        assert len(content_type['available_statuses']) == 4
125
-
126
-        content_type = res[3]
127
-        assert content_type['slug'] == 'html-document'
128
-        assert content_type['fa_icon'] == 'file-text-o'
129
-        assert content_type['hexcolor'] == '#3f52e3'
130
-        assert content_type['label'] == 'Text Document'
131
-        assert content_type['creation_label'] == 'Write a document'
132
-        assert 'available_statuses' in content_type
133
-        assert len(content_type['available_statuses']) == 4
134
-        # TODO - G.M - 31-05-2018 - Check Folder type
135
-        # TODO - G.M - 29-05-2018 - Better check for available_statuses
77
+        for counter, content_type_slug in enumerate(content_types):
78
+            content_type = CONTENT_TYPES.get_one_by_slug(content_type_slug)
79
+            assert res[counter]['slug'] == content_type.slug
80
+            assert res[counter]['fa_icon'] == content_type.fa_icon
81
+            assert res[counter]['hexcolor'] == content_type.hexcolor
82
+            assert res[counter]['label'] == content_type.label
83
+            assert res[counter]['creation_label'] == content_type.creation_label
84
+            for status_counter, status in enumerate(content_type.available_statuses):
85
+                assert res[counter]['available_statuses'][status_counter]['fa_icon'] == status.fa_icon  # nopep8
86
+                assert res[counter]['available_statuses'][status_counter]['global_status'] == status.global_status  # nopep8
87
+                assert res[counter]['available_statuses'][status_counter]['slug'] == status.slug  # nopep8
88
+                assert res[counter]['available_statuses'][status_counter]['hexcolor'] == status.hexcolor  # nopep8
136
 
89
 
137
     def test_api__get_content_types__err_401__unregistered_user(self):
90
     def test_api__get_content_types__err_401__unregistered_user(self):
138
         """
91
         """

+ 525 - 127
backend/tracim_backend/tests/functional/test_user.py View File

13
 from tracim_backend.lib.core.userworkspace import RoleApi
13
 from tracim_backend.lib.core.userworkspace import RoleApi
14
 from tracim_backend.lib.core.workspace import WorkspaceApi
14
 from tracim_backend.lib.core.workspace import WorkspaceApi
15
 from tracim_backend.models import get_tm_session
15
 from tracim_backend.models import get_tm_session
16
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
16
+from tracim_backend.models.contents import CONTENT_TYPES
17
 from tracim_backend.models.data import UserRoleInWorkspace
17
 from tracim_backend.models.data import UserRoleInWorkspace
18
 from tracim_backend.models.revision_protection import new_revision
18
 from tracim_backend.models.revision_protection import new_revision
19
 from tracim_backend.tests import FunctionalTest
19
 from tracim_backend.tests import FunctionalTest
87
             session=dbsession,
87
             session=dbsession,
88
             config=self.app_config,
88
             config=self.app_config,
89
         )
89
         )
90
-        main_folder_workspace2 = api.create(ContentType.Folder, workspace2, None, 'Hepla', '', True)  # nopep8
91
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
90
+        main_folder_workspace2 = api.create(CONTENT_TYPES.Folder.slug, workspace2, None, 'Hepla', '', True)  # nopep8
91
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
92
         # creation order test
92
         # creation order test
93
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
94
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
93
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
94
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
95
         # update order test
95
         # update order test
96
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
97
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
96
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
97
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
98
         with new_revision(
98
         with new_revision(
99
             session=dbsession,
99
             session=dbsession,
100
             tm=transaction.manager,
100
             tm=transaction.manager,
103
             firstly_created_but_recently_updated.description = 'Just an update'
103
             firstly_created_but_recently_updated.description = 'Just an update'
104
         api.save(firstly_created_but_recently_updated)
104
         api.save(firstly_created_but_recently_updated)
105
         # comment change order
105
         # comment change order
106
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
107
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
106
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
107
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
108
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
108
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
109
-        content_workspace_2 = api.create(ContentType.Page, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
109
+        content_workspace_2 = api.create(CONTENT_TYPES.Page.slug, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
110
         dbsession.flush()
110
         dbsession.flush()
111
         transaction.commit()
111
         transaction.commit()
112
 
112
 
205
             session=dbsession,
205
             session=dbsession,
206
             config=self.app_config,
206
             config=self.app_config,
207
         )
207
         )
208
-        main_folder_workspace2 = api.create(ContentType.Folder, workspace2, None, 'Hepla', '', True)  # nopep8
209
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
208
+        main_folder_workspace2 = api.create(CONTENT_TYPES.Folder.slug, workspace2, None, 'Hepla', '', True)  # nopep8
209
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
210
         # creation order test
210
         # creation order test
211
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
212
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
211
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
212
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
213
         # update order test
213
         # update order test
214
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
215
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
214
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
215
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
216
+
216
         with new_revision(
217
         with new_revision(
217
             session=dbsession,
218
             session=dbsession,
218
             tm=transaction.manager,
219
             tm=transaction.manager,
221
             firstly_created_but_recently_updated.description = 'Just an update'
222
             firstly_created_but_recently_updated.description = 'Just an update'
222
         api.save(firstly_created_but_recently_updated)
223
         api.save(firstly_created_but_recently_updated)
223
         # comment change order
224
         # comment change order
224
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
225
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
225
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
226
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
226
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
227
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
227
-        content_workspace_2 = api.create(ContentType.Page, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
228
+        content_workspace_2 = api.create(CONTENT_TYPES.Page.slug, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
228
         dbsession.flush()
229
         dbsession.flush()
229
         transaction.commit()
230
         transaction.commit()
230
 
231
 
301
             session=dbsession,
302
             session=dbsession,
302
             config=self.app_config,
303
             config=self.app_config,
303
         )
304
         )
304
-        main_folder_workspace2 = api.create(ContentType.Folder, workspace2, None, 'Hepla', '', True)  # nopep8
305
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
305
+        main_folder_workspace2 = api.create(CONTENT_TYPES.Folder.slug, workspace2, None, 'Hepla', '', True)  # nopep8
306
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
306
         # creation order test
307
         # creation order test
307
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
308
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
308
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
309
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
309
         # update order test
310
         # update order test
310
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
311
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
311
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
312
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
312
         with new_revision(
313
         with new_revision(
313
             session=dbsession,
314
             session=dbsession,
314
             tm=transaction.manager,
315
             tm=transaction.manager,
317
             firstly_created_but_recently_updated.description = 'Just an update'
318
             firstly_created_but_recently_updated.description = 'Just an update'
318
         api.save(firstly_created_but_recently_updated)
319
         api.save(firstly_created_but_recently_updated)
319
         # comment change order
320
         # comment change order
320
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
321
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
321
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
322
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
322
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
323
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
323
-        content_workspace_2 = api.create(ContentType.Page, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
324
+        content_workspace_2 = api.create(CONTENT_TYPES.Page.slug, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
324
         dbsession.flush()
325
         dbsession.flush()
325
         transaction.commit()
326
         transaction.commit()
326
 
327
 
426
             session=dbsession,
427
             session=dbsession,
427
             config=self.app_config,
428
             config=self.app_config,
428
         )
429
         )
429
-        main_folder_workspace2 = api.create(ContentType.Folder, workspace2, None, 'Hepla', '', True)  # nopep8
430
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
430
+        main_folder_workspace2 = api.create(CONTENT_TYPES.Folder.slug, workspace2, None, 'Hepla', '', True)  # nopep8
431
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
431
         # creation order test
432
         # creation order test
432
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
433
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
433
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
434
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
434
         # update order test
435
         # update order test
435
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
436
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
436
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
437
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
437
         with new_revision(
438
         with new_revision(
438
             session=dbsession,
439
             session=dbsession,
439
             tm=transaction.manager,
440
             tm=transaction.manager,
442
             firstly_created_but_recently_updated.description = 'Just an update'
443
             firstly_created_but_recently_updated.description = 'Just an update'
443
         api.save(firstly_created_but_recently_updated)
444
         api.save(firstly_created_but_recently_updated)
444
         # comment change order
445
         # comment change order
445
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
446
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
446
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
447
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
447
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
448
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
448
-        content_workspace_2 = api.create(ContentType.Page, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
449
+        content_workspace_2 = api.create(CONTENT_TYPES.Page.slug, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
449
         dbsession.flush()
450
         dbsession.flush()
450
         transaction.commit()
451
         transaction.commit()
451
 
452
 
498
             session=dbsession,
499
             session=dbsession,
499
             config=self.app_config,
500
             config=self.app_config,
500
         )
501
         )
501
-        main_folder_workspace2 = api.create(ContentType.Folder, workspace2, None, 'Hepla', '', True)  # nopep8
502
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
502
+        main_folder_workspace2 = api.create(CONTENT_TYPES.Folder.slug, workspace2, None, 'Hepla', '', True)  # nopep8
503
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
503
         # creation order test
504
         # creation order test
504
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
505
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
505
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
506
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
506
         # update order test
507
         # update order test
507
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
508
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
508
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
509
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
509
         with new_revision(
510
         with new_revision(
510
             session=dbsession,
511
             session=dbsession,
511
             tm=transaction.manager,
512
             tm=transaction.manager,
514
             firstly_created_but_recently_updated.description = 'Just an update'
515
             firstly_created_but_recently_updated.description = 'Just an update'
515
         api.save(firstly_created_but_recently_updated)
516
         api.save(firstly_created_but_recently_updated)
516
         # comment change order
517
         # comment change order
517
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
518
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
518
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
519
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
519
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
520
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
520
-        content_workspace_2 = api.create(ContentType.Page, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
521
+        content_workspace_2 = api.create(CONTENT_TYPES.Page.slug, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
521
         dbsession.flush()
522
         dbsession.flush()
522
         transaction.commit()
523
         transaction.commit()
523
 
524
 
610
             session=dbsession,
611
             session=dbsession,
611
             config=self.app_config,
612
             config=self.app_config,
612
         )
613
         )
613
-        main_folder_workspace2 = api.create(ContentType.Folder, workspace2, None, 'Hepla', '', True)  # nopep8
614
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
614
+        main_folder_workspace2 = api.create(CONTENT_TYPES.Folder.slug, workspace2, None, 'Hepla', '', True)  # nopep8
615
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
615
         # creation order test
616
         # creation order test
616
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
617
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
617
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
618
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
618
         # update order test
619
         # update order test
619
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
620
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
620
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
621
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
621
         with new_revision(
622
         with new_revision(
622
             session=dbsession,
623
             session=dbsession,
623
             tm=transaction.manager,
624
             tm=transaction.manager,
626
             firstly_created_but_recently_updated.description = 'Just an update'
627
             firstly_created_but_recently_updated.description = 'Just an update'
627
         api.save(firstly_created_but_recently_updated)
628
         api.save(firstly_created_but_recently_updated)
628
         # comment change order
629
         # comment change order
629
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
630
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
630
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
631
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
631
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
632
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
632
-        content_workspace_2 = api.create(ContentType.Page, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
633
+        content_workspace_2 = api.create(CONTENT_TYPES.Page.slug, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
633
         dbsession.flush()
634
         dbsession.flush()
634
         transaction.commit()
635
         transaction.commit()
635
 
636
 
714
             session=dbsession,
715
             session=dbsession,
715
             config=self.app_config,
716
             config=self.app_config,
716
         )
717
         )
717
-        main_folder_workspace2 = api.create(ContentType.Folder, workspace2, None, 'Hepla', '', True)  # nopep8
718
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
718
+        main_folder_workspace2 = api.create(CONTENT_TYPES.Folder.slug, workspace2, None, 'Hepla', '', True)  # nopep8
719
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
719
         # creation order test
720
         # creation order test
720
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
721
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
721
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
722
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
722
         # update order test
723
         # update order test
723
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
724
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
724
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
725
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
725
         with new_revision(
726
         with new_revision(
726
             session=dbsession,
727
             session=dbsession,
727
             tm=transaction.manager,
728
             tm=transaction.manager,
730
             firstly_created_but_recently_updated.description = 'Just an update'
731
             firstly_created_but_recently_updated.description = 'Just an update'
731
         api.save(firstly_created_but_recently_updated)
732
         api.save(firstly_created_but_recently_updated)
732
         # comment change order
733
         # comment change order
733
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
734
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
734
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
735
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
735
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
736
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
736
-        content_workspace_2 = api.create(ContentType.Page, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
737
+        content_workspace_2 = api.create(CONTENT_TYPES.Page.slug, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
737
         dbsession.flush()
738
         dbsession.flush()
738
         transaction.commit()
739
         transaction.commit()
739
 
740
 
826
             session=dbsession,
827
             session=dbsession,
827
             config=self.app_config,
828
             config=self.app_config,
828
         )
829
         )
829
-        main_folder_workspace2 = api.create(ContentType.Folder, workspace2, None, 'Hepla', '', True)  # nopep8
830
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
830
+        main_folder_workspace2 = api.create(CONTENT_TYPES.Folder.slug, workspace2, None, 'Hepla', '', True)  # nopep8
831
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
831
         # creation order test
832
         # creation order test
832
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
833
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
833
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
834
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
834
         # update order test
835
         # update order test
835
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
836
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
836
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
837
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
837
         with new_revision(
838
         with new_revision(
838
             session=dbsession,
839
             session=dbsession,
839
             tm=transaction.manager,
840
             tm=transaction.manager,
842
             firstly_created_but_recently_updated.description = 'Just an update'
843
             firstly_created_but_recently_updated.description = 'Just an update'
843
         api.save(firstly_created_but_recently_updated)
844
         api.save(firstly_created_but_recently_updated)
844
         # comment change order
845
         # comment change order
845
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
846
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
846
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
847
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
847
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
848
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
848
-        content_workspace_2 = api.create(ContentType.Page, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
849
+        content_workspace_2 = api.create(CONTENT_TYPES.Page.slug, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
849
         dbsession.flush()
850
         dbsession.flush()
850
         transaction.commit()
851
         transaction.commit()
851
 
852
 
949
             session=dbsession,
950
             session=dbsession,
950
             config=self.app_config,
951
             config=self.app_config,
951
         )
952
         )
952
-        main_folder_workspace2 = api.create(ContentType.Folder, workspace2, None, 'Hepla', '', True)  # nopep8
953
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
953
+        main_folder_workspace2 = api.create(CONTENT_TYPES.Folder.slug, workspace2, None, 'Hepla', '', True)  # nopep8
954
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
954
         # creation order test
955
         # creation order test
955
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
956
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
956
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
957
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
957
         # update order test
958
         # update order test
958
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
959
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
959
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
960
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
960
         with new_revision(
961
         with new_revision(
961
             session=dbsession,
962
             session=dbsession,
962
             tm=transaction.manager,
963
             tm=transaction.manager,
965
             firstly_created_but_recently_updated.description = 'Just an update'
966
             firstly_created_but_recently_updated.description = 'Just an update'
966
         api.save(firstly_created_but_recently_updated)
967
         api.save(firstly_created_but_recently_updated)
967
         # comment change order
968
         # comment change order
968
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
969
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
969
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
970
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
970
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
971
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
971
-        content_workspace_2 = api.create(ContentType.Page, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
972
+        content_workspace_2 = api.create(CONTENT_TYPES.Page.slug, workspace2,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
972
         dbsession.flush()
973
         dbsession.flush()
973
         transaction.commit()
974
         transaction.commit()
974
 
975
 
1059
             session=dbsession,
1060
             session=dbsession,
1060
             config=self.app_config,
1061
             config=self.app_config,
1061
         )
1062
         )
1062
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
1063
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
1063
         # creation order test
1064
         # creation order test
1064
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1065
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1065
         api.mark_unread(firstly_created)
1066
         api.mark_unread(firstly_created)
1066
         api2.mark_unread(firstly_created)
1067
         api2.mark_unread(firstly_created)
1067
         dbsession.flush()
1068
         dbsession.flush()
1167
             session=dbsession,
1168
             session=dbsession,
1168
             config=self.app_config,
1169
             config=self.app_config,
1169
         )
1170
         )
1170
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
1171
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
1171
         # creation order test
1172
         # creation order test
1172
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1173
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1173
         api.mark_unread(firstly_created)
1174
         api.mark_unread(firstly_created)
1174
         api2.mark_unread(firstly_created)
1175
         api2.mark_unread(firstly_created)
1175
         dbsession.flush()
1176
         dbsession.flush()
1248
             session=dbsession,
1249
             session=dbsession,
1249
             config=self.app_config,
1250
             config=self.app_config,
1250
         )
1251
         )
1251
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
1252
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
1252
         # creation order test
1253
         # creation order test
1253
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1254
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1254
         api.mark_unread(firstly_created)
1255
         api.mark_unread(firstly_created)
1255
         api2.mark_unread(firstly_created)
1256
         api2.mark_unread(firstly_created)
1256
         dbsession.flush()
1257
         dbsession.flush()
1329
             session=dbsession,
1330
             session=dbsession,
1330
             config=self.app_config,
1331
             config=self.app_config,
1331
         )
1332
         )
1332
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
1333
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
1333
         # creation order test
1334
         # creation order test
1334
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1335
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1335
         api.mark_unread(firstly_created)
1336
         api.mark_unread(firstly_created)
1336
         api2.mark_unread(firstly_created)
1337
         api2.mark_unread(firstly_created)
1337
         dbsession.flush()
1338
         dbsession.flush()
1428
             session=dbsession,
1429
             session=dbsession,
1429
             config=self.app_config,
1430
             config=self.app_config,
1430
         )
1431
         )
1431
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
1432
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
1432
         # creation order test
1433
         # creation order test
1433
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1434
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1434
         api.mark_unread(firstly_created)
1435
         api.mark_unread(firstly_created)
1435
         api2.mark_unread(firstly_created)
1436
         api2.mark_unread(firstly_created)
1436
         dbsession.flush()
1437
         dbsession.flush()
1509
             session=dbsession,
1510
             session=dbsession,
1510
             config=self.app_config,
1511
             config=self.app_config,
1511
         )
1512
         )
1512
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
1513
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
1513
         # creation order test
1514
         # creation order test
1514
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1515
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1515
         comments = api.create_comment(workspace, firstly_created, 'juste a super comment', True)  # nopep8
1516
         comments = api.create_comment(workspace, firstly_created, 'juste a super comment', True)  # nopep8
1516
         api.mark_unread(firstly_created)
1517
         api.mark_unread(firstly_created)
1517
         api.mark_unread(comments)
1518
         api.mark_unread(comments)
1626
             session=dbsession,
1627
             session=dbsession,
1627
             config=self.app_config,
1628
             config=self.app_config,
1628
         )
1629
         )
1629
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
1630
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
1630
         # creation order test
1631
         # creation order test
1631
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1632
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1632
         api.mark_read(firstly_created)
1633
         api.mark_read(firstly_created)
1633
         api2.mark_read(firstly_created)
1634
         api2.mark_read(firstly_created)
1634
         dbsession.flush()
1635
         dbsession.flush()
1735
             session=dbsession,
1736
             session=dbsession,
1736
             config=self.app_config,
1737
             config=self.app_config,
1737
         )
1738
         )
1738
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
1739
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
1739
         # creation order test
1740
         # creation order test
1740
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1741
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1741
         api.mark_read(firstly_created)
1742
         api.mark_read(firstly_created)
1742
         api2.mark_read(firstly_created)
1743
         api2.mark_read(firstly_created)
1743
         dbsession.flush()
1744
         dbsession.flush()
1816
             session=dbsession,
1817
             session=dbsession,
1817
             config=self.app_config,
1818
             config=self.app_config,
1818
         )
1819
         )
1819
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
1820
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
1820
         # creation order test
1821
         # creation order test
1821
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1822
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1822
         api.mark_read(firstly_created)
1823
         api.mark_read(firstly_created)
1823
         api2.mark_read(firstly_created)
1824
         api2.mark_read(firstly_created)
1824
         dbsession.flush()
1825
         dbsession.flush()
1898
             session=dbsession,
1899
             session=dbsession,
1899
             config=self.app_config,
1900
             config=self.app_config,
1900
         )
1901
         )
1901
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
1902
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
1902
         # creation order test
1903
         # creation order test
1903
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1904
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1904
         api.mark_read(firstly_created)
1905
         api.mark_read(firstly_created)
1905
         api2.mark_read(firstly_created)
1906
         api2.mark_read(firstly_created)
1906
         dbsession.flush()
1907
         dbsession.flush()
1993
             session=dbsession,
1994
             session=dbsession,
1994
             config=self.app_config,
1995
             config=self.app_config,
1995
         )
1996
         )
1996
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
1997
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
1997
         # creation order test
1998
         # creation order test
1998
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1999
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
1999
         api.mark_read(firstly_created)
2000
         api.mark_read(firstly_created)
2000
         api2.mark_read(firstly_created)
2001
         api2.mark_read(firstly_created)
2001
         dbsession.flush()
2002
         dbsession.flush()
2044
             session=dbsession,
2045
             session=dbsession,
2045
             config=self.app_config,
2046
             config=self.app_config,
2046
         )
2047
         )
2047
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
2048
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
2048
         # creation order test
2049
         # creation order test
2049
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2050
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2050
         comments = api.create_comment(workspace, firstly_created, 'juste a super comment', True)  # nopep8
2051
         comments = api.create_comment(workspace, firstly_created, 'juste a super comment', True)  # nopep8
2051
         api.mark_read(firstly_created)
2052
         api.mark_read(firstly_created)
2052
         api.mark_read(comments)
2053
         api.mark_read(comments)
2137
             session=dbsession,
2138
             session=dbsession,
2138
             config=self.app_config,
2139
             config=self.app_config,
2139
         )
2140
         )
2140
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
2141
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
2141
         # creation order test
2142
         # creation order test
2142
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2143
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2143
         api.mark_unread(main_folder)
2144
         api.mark_unread(main_folder)
2144
         api.mark_unread(firstly_created)
2145
         api.mark_unread(firstly_created)
2145
         api2.mark_unread(main_folder)
2146
         api2.mark_unread(main_folder)
2234
             session=dbsession,
2235
             session=dbsession,
2235
             config=self.app_config,
2236
             config=self.app_config,
2236
         )
2237
         )
2237
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
2238
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
2238
         # creation order test
2239
         # creation order test
2239
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2240
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2240
         api.mark_unread(main_folder)
2241
         api.mark_unread(main_folder)
2241
         api.mark_unread(firstly_created)
2242
         api.mark_unread(firstly_created)
2242
         api2.mark_unread(main_folder)
2243
         api2.mark_unread(main_folder)
2331
             session=dbsession,
2332
             session=dbsession,
2332
             config=self.app_config,
2333
             config=self.app_config,
2333
         )
2334
         )
2334
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
2335
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
2335
         # creation order test
2336
         # creation order test
2336
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2337
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2337
         api.mark_unread(main_folder)
2338
         api.mark_unread(main_folder)
2338
         api.mark_unread(firstly_created)
2339
         api.mark_unread(firstly_created)
2339
         api2.mark_unread(main_folder)
2340
         api2.mark_unread(main_folder)
2381
         assert workspace['workspace_id'] == 1
2382
         assert workspace['workspace_id'] == 1
2382
         assert workspace['label'] == 'Business'
2383
         assert workspace['label'] == 'Business'
2383
         assert workspace['slug'] == 'business'
2384
         assert workspace['slug'] == 'business'
2384
-        assert len(workspace['sidebar_entries']) == 7
2385
+        assert len(workspace['sidebar_entries']) == 5
2385
 
2386
 
2387
+        # TODO - G.M - 2018-08-02 - Better test for sidebar entry, make it
2388
+        # not fixed on active application/content-file
2386
         sidebar_entry = workspace['sidebar_entries'][0]
2389
         sidebar_entry = workspace['sidebar_entries'][0]
2387
         assert sidebar_entry['slug'] == 'dashboard'
2390
         assert sidebar_entry['slug'] == 'dashboard'
2388
         assert sidebar_entry['label'] == 'Dashboard'
2391
         assert sidebar_entry['label'] == 'Dashboard'
2405
         assert sidebar_entry['fa_icon'] == "file-text-o"
2408
         assert sidebar_entry['fa_icon'] == "file-text-o"
2406
 
2409
 
2407
         sidebar_entry = workspace['sidebar_entries'][3]
2410
         sidebar_entry = workspace['sidebar_entries'][3]
2408
-        assert sidebar_entry['slug'] == 'contents/markdownpluspage'
2409
-        assert sidebar_entry['label'] == 'Markdown Plus Documents'
2410
-        assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=markdownpluspage"    # nopep8
2411
-        assert sidebar_entry['hexcolor'] == "#f12d2d"
2412
-        assert sidebar_entry['fa_icon'] == "file-code-o"
2413
-
2414
-        sidebar_entry = workspace['sidebar_entries'][4]
2415
         assert sidebar_entry['slug'] == 'contents/file'
2411
         assert sidebar_entry['slug'] == 'contents/file'
2416
         assert sidebar_entry['label'] == 'Files'
2412
         assert sidebar_entry['label'] == 'Files'
2417
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=file"  # nopep8
2413
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=file"  # nopep8
2418
         assert sidebar_entry['hexcolor'] == "#FF9900"
2414
         assert sidebar_entry['hexcolor'] == "#FF9900"
2419
         assert sidebar_entry['fa_icon'] == "paperclip"
2415
         assert sidebar_entry['fa_icon'] == "paperclip"
2420
 
2416
 
2421
-        sidebar_entry = workspace['sidebar_entries'][5]
2417
+        sidebar_entry = workspace['sidebar_entries'][4]
2422
         assert sidebar_entry['slug'] == 'contents/thread'
2418
         assert sidebar_entry['slug'] == 'contents/thread'
2423
         assert sidebar_entry['label'] == 'Threads'
2419
         assert sidebar_entry['label'] == 'Threads'
2424
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=thread"  # nopep8
2420
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=thread"  # nopep8
2425
         assert sidebar_entry['hexcolor'] == "#ad4cf9"
2421
         assert sidebar_entry['hexcolor'] == "#ad4cf9"
2426
         assert sidebar_entry['fa_icon'] == "comments-o"
2422
         assert sidebar_entry['fa_icon'] == "comments-o"
2427
 
2423
 
2428
-        sidebar_entry = workspace['sidebar_entries'][6]
2429
-        assert sidebar_entry['slug'] == 'calendar'
2430
-        assert sidebar_entry['label'] == 'Calendar'
2431
-        assert sidebar_entry['route'] == "/#/workspaces/1/calendar"  # nopep8
2432
-        assert sidebar_entry['hexcolor'] == "#757575"
2433
-        assert sidebar_entry['fa_icon'] == "calendar"
2434
 
2424
 
2435
     def test_api__get_user_workspaces__err_403__unallowed_user(self):
2425
     def test_api__get_user_workspaces__err_403__unallowed_user(self):
2436
         """
2426
         """
2645
         )
2635
         )
2646
 
2636
 
2647
 
2637
 
2638
+class TestUsersEndpoint(FunctionalTest):
2639
+    # -*- coding: utf-8 -*-
2640
+    """
2641
+    Tests for GET /api/v2/users/{user_id}
2642
+    """
2643
+    fixtures = [BaseFixture]
2644
+
2645
+    def test_api__get_user__ok_200__admin(self):
2646
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
2647
+        admin = dbsession.query(models.User) \
2648
+            .filter(models.User.email == 'admin@admin.admin') \
2649
+            .one()
2650
+        uapi = UserApi(
2651
+            current_user=admin,
2652
+            session=dbsession,
2653
+            config=self.app_config,
2654
+        )
2655
+        gapi = GroupApi(
2656
+            current_user=admin,
2657
+            session=dbsession,
2658
+            config=self.app_config,
2659
+        )
2660
+        groups = [gapi.get_one_with_name('users')]
2661
+        test_user = uapi.create_user(
2662
+            email='test@test.test',
2663
+            password='pass',
2664
+            name='bob',
2665
+            groups=groups,
2666
+            timezone='Europe/Paris',
2667
+            do_save=True,
2668
+            do_notify=False,
2669
+        )
2670
+        uapi.save(test_user)
2671
+        transaction.commit()
2672
+        user_id = int(test_user.user_id)
2673
+
2674
+        self.testapp.authorization = (
2675
+            'Basic',
2676
+            (
2677
+                'admin@admin.admin',
2678
+                'admin@admin.admin'
2679
+            )
2680
+        )
2681
+        res = self.testapp.get(
2682
+            '/api/v2/users',
2683
+            status=200
2684
+        )
2685
+        res = res.json_body
2686
+        assert len(res) == 2
2687
+        assert res[0]['user_id'] == admin.user_id
2688
+        assert res[0]['public_name'] == admin.display_name
2689
+        assert res[0]['avatar_url'] is None
2690
+
2691
+        assert res[1]['user_id'] == test_user.user_id
2692
+        assert res[1]['public_name'] == test_user.display_name
2693
+        assert res[1]['avatar_url'] is None
2694
+
2695
+    def test_api__get_user__err_403__normal_user(self):
2696
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
2697
+        admin = dbsession.query(models.User) \
2698
+            .filter(models.User.email == 'admin@admin.admin') \
2699
+            .one()
2700
+        uapi = UserApi(
2701
+            current_user=admin,
2702
+            session=dbsession,
2703
+            config=self.app_config,
2704
+        )
2705
+        gapi = GroupApi(
2706
+            current_user=admin,
2707
+            session=dbsession,
2708
+            config=self.app_config,
2709
+        )
2710
+        groups = [gapi.get_one_with_name('users')]
2711
+        test_user = uapi.create_user(
2712
+            email='test@test.test',
2713
+            password='pass',
2714
+            name='bob',
2715
+            groups=groups,
2716
+            timezone='Europe/Paris',
2717
+            do_save=True,
2718
+            do_notify=False,
2719
+        )
2720
+        uapi.save(test_user)
2721
+        transaction.commit()
2722
+        user_id = int(test_user.user_id)
2723
+
2724
+        self.testapp.authorization = (
2725
+            'Basic',
2726
+            (
2727
+                'test@test.test',
2728
+                'pass'
2729
+            )
2730
+        )
2731
+        self.testapp.get(
2732
+            '/api/v2/users',
2733
+            status=403
2734
+        )
2735
+
2736
+
2737
+class TestKnownMembersEndpoint(FunctionalTest):
2738
+    # -*- coding: utf-8 -*-
2739
+    """
2740
+    Tests for GET /api/v2/users/{user_id}
2741
+    """
2742
+    fixtures = [BaseFixture]
2743
+
2744
+    def test_api__get_user__ok_200__admin__by_name(self):
2745
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
2746
+        admin = dbsession.query(models.User) \
2747
+            .filter(models.User.email == 'admin@admin.admin') \
2748
+            .one()
2749
+        uapi = UserApi(
2750
+            current_user=admin,
2751
+            session=dbsession,
2752
+            config=self.app_config,
2753
+        )
2754
+        gapi = GroupApi(
2755
+            current_user=admin,
2756
+            session=dbsession,
2757
+            config=self.app_config,
2758
+        )
2759
+        groups = [gapi.get_one_with_name('users')]
2760
+        test_user = uapi.create_user(
2761
+            email='test@test.test',
2762
+            password='pass',
2763
+            name='bob',
2764
+            groups=groups,
2765
+            timezone='Europe/Paris',
2766
+            do_save=True,
2767
+            do_notify=False,
2768
+        )
2769
+        test_user2 = uapi.create_user(
2770
+            email='test2@test2.test2',
2771
+            password='pass',
2772
+            name='bob2',
2773
+            groups=groups,
2774
+            timezone='Europe/Paris',
2775
+            do_save=True,
2776
+            do_notify=False,
2777
+        )
2778
+        uapi.save(test_user)
2779
+        uapi.save(test_user2)
2780
+        transaction.commit()
2781
+        user_id = int(admin.user_id)
2782
+
2783
+        self.testapp.authorization = (
2784
+            'Basic',
2785
+            (
2786
+                'admin@admin.admin',
2787
+                'admin@admin.admin'
2788
+            )
2789
+        )
2790
+        params = {
2791
+            'acp': 'bob',
2792
+        }
2793
+        res = self.testapp.get(
2794
+            '/api/v2/users/{user_id}/known_members'.format(user_id=user_id),
2795
+            status=200,
2796
+            params=params,
2797
+        )
2798
+        res = res.json_body
2799
+        assert len(res) == 2
2800
+        assert res[0]['user_id'] == test_user.user_id
2801
+        assert res[0]['public_name'] == test_user.display_name
2802
+        assert res[0]['avatar_url'] is None
2803
+
2804
+        assert res[1]['user_id'] == test_user2.user_id
2805
+        assert res[1]['public_name'] == test_user2.display_name
2806
+        assert res[1]['avatar_url'] is None
2807
+
2808
+    def test_api__get_user__ok_200__admin__by_email(self):
2809
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
2810
+        admin = dbsession.query(models.User) \
2811
+            .filter(models.User.email == 'admin@admin.admin') \
2812
+            .one()
2813
+        uapi = UserApi(
2814
+            current_user=admin,
2815
+            session=dbsession,
2816
+            config=self.app_config,
2817
+        )
2818
+        gapi = GroupApi(
2819
+            current_user=admin,
2820
+            session=dbsession,
2821
+            config=self.app_config,
2822
+        )
2823
+        groups = [gapi.get_one_with_name('users')]
2824
+        test_user = uapi.create_user(
2825
+            email='test@test.test',
2826
+            password='pass',
2827
+            name='bob',
2828
+            groups=groups,
2829
+            timezone='Europe/Paris',
2830
+            do_save=True,
2831
+            do_notify=False,
2832
+        )
2833
+        test_user2 = uapi.create_user(
2834
+            email='test2@test2.test2',
2835
+            password='pass',
2836
+            name='bob2',
2837
+            groups=groups,
2838
+            timezone='Europe/Paris',
2839
+            do_save=True,
2840
+            do_notify=False,
2841
+        )
2842
+        uapi.save(test_user)
2843
+        uapi.save(test_user2)
2844
+        transaction.commit()
2845
+        user_id = int(admin.user_id)
2846
+
2847
+        self.testapp.authorization = (
2848
+            'Basic',
2849
+            (
2850
+                'admin@admin.admin',
2851
+                'admin@admin.admin'
2852
+            )
2853
+        )
2854
+        params = {
2855
+            'acp': 'test',
2856
+        }
2857
+        res = self.testapp.get(
2858
+            '/api/v2/users/{user_id}/known_members'.format(user_id=user_id),
2859
+            status=200,
2860
+            params=params,
2861
+        )
2862
+        res = res.json_body
2863
+        assert len(res) == 2
2864
+        assert res[0]['user_id'] == test_user.user_id
2865
+        assert res[0]['public_name'] == test_user.display_name
2866
+        assert res[0]['avatar_url'] is None
2867
+
2868
+        assert res[1]['user_id'] == test_user2.user_id
2869
+        assert res[1]['public_name'] == test_user2.display_name
2870
+        assert res[1]['avatar_url'] is None
2871
+
2872
+    def test_api__get_user__err_403__admin__too_small_acp(self):
2873
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
2874
+        admin = dbsession.query(models.User) \
2875
+            .filter(models.User.email == 'admin@admin.admin') \
2876
+            .one()
2877
+        uapi = UserApi(
2878
+            current_user=admin,
2879
+            session=dbsession,
2880
+            config=self.app_config,
2881
+        )
2882
+        gapi = GroupApi(
2883
+            current_user=admin,
2884
+            session=dbsession,
2885
+            config=self.app_config,
2886
+        )
2887
+        groups = [gapi.get_one_with_name('users')]
2888
+        test_user = uapi.create_user(
2889
+            email='test@test.test',
2890
+            password='pass',
2891
+            name='bob',
2892
+            groups=groups,
2893
+            timezone='Europe/Paris',
2894
+            do_save=True,
2895
+            do_notify=False,
2896
+        )
2897
+        test_user2 = uapi.create_user(
2898
+            email='test2@test2.test2',
2899
+            password='pass',
2900
+            name='bob2',
2901
+            groups=groups,
2902
+            timezone='Europe/Paris',
2903
+            do_save=True,
2904
+            do_notify=False,
2905
+        )
2906
+        uapi.save(test_user)
2907
+        transaction.commit()
2908
+        user_id = int(admin.user_id)
2909
+
2910
+        self.testapp.authorization = (
2911
+            'Basic',
2912
+            (
2913
+                'admin@admin.admin',
2914
+                'admin@admin.admin'
2915
+            )
2916
+        )
2917
+        params = {
2918
+            'acp': 't',
2919
+        }
2920
+        res = self.testapp.get(
2921
+            '/api/v2/users/{user_id}/known_members'.format(user_id=user_id),
2922
+            status=400,
2923
+            params=params
2924
+        )
2925
+
2926
+    def test_api__get_user__ok_200__normal_user_by_email(self):
2927
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
2928
+        admin = dbsession.query(models.User) \
2929
+            .filter(models.User.email == 'admin@admin.admin') \
2930
+            .one()
2931
+        uapi = UserApi(
2932
+            current_user=admin,
2933
+            session=dbsession,
2934
+            config=self.app_config,
2935
+        )
2936
+        gapi = GroupApi(
2937
+            current_user=admin,
2938
+            session=dbsession,
2939
+            config=self.app_config,
2940
+        )
2941
+        groups = [gapi.get_one_with_name('users')]
2942
+        test_user = uapi.create_user(
2943
+            email='test@test.test',
2944
+            password='pass',
2945
+            name='bob',
2946
+            groups=groups,
2947
+            timezone='Europe/Paris',
2948
+            do_save=True,
2949
+            do_notify=False,
2950
+        )
2951
+        test_user2 = uapi.create_user(
2952
+            email='test2@test2.test2',
2953
+            password='pass',
2954
+            name='bob2',
2955
+            groups=groups,
2956
+            timezone='Europe/Paris',
2957
+            do_save=True,
2958
+            do_notify=False,
2959
+        )
2960
+        test_user3 = uapi.create_user(
2961
+            email='test3@test3.test3',
2962
+            password='pass',
2963
+            name='bob3',
2964
+            groups=groups,
2965
+            timezone='Europe/Paris',
2966
+            do_save=True,
2967
+            do_notify=False,
2968
+        )
2969
+        uapi.save(test_user)
2970
+        uapi.save(test_user2)
2971
+        uapi.save(test_user3)
2972
+        workspace_api = WorkspaceApi(
2973
+            current_user=admin,
2974
+            session=dbsession,
2975
+            config=self.app_config
2976
+
2977
+        )
2978
+        workspace = WorkspaceApi(
2979
+            current_user=admin,
2980
+            session=dbsession,
2981
+            config=self.app_config,
2982
+        ).create_workspace(
2983
+            'test workspace',
2984
+            save_now=True
2985
+        )
2986
+        role_api = RoleApi(
2987
+            current_user=admin,
2988
+            session=dbsession,
2989
+            config=self.app_config,
2990
+        )
2991
+        role_api.create_one(test_user, workspace, UserRoleInWorkspace.READER, False)
2992
+        role_api.create_one(test_user2, workspace, UserRoleInWorkspace.READER, False)
2993
+        transaction.commit()
2994
+        user_id = int(test_user.user_id)
2995
+
2996
+        self.testapp.authorization = (
2997
+            'Basic',
2998
+            (
2999
+                'test@test.test',
3000
+                'pass'
3001
+            )
3002
+        )
3003
+        params = {
3004
+            'acp': 'test',
3005
+        }
3006
+        res = self.testapp.get(
3007
+            '/api/v2/users/{user_id}/known_members'.format(user_id=user_id),
3008
+            status=200,
3009
+            params=params
3010
+        )
3011
+        res = res.json_body
3012
+        assert len(res) == 2
3013
+        assert res[0]['user_id'] == test_user.user_id
3014
+        assert res[0]['public_name'] == test_user.display_name
3015
+        assert res[0]['avatar_url'] is None
3016
+
3017
+        assert res[1]['user_id'] == test_user2.user_id
3018
+        assert res[1]['public_name'] == test_user2.display_name
3019
+        assert res[1]['avatar_url'] is None
3020
+
3021
+
2648
 class TestSetEmailEndpoint(FunctionalTest):
3022
 class TestSetEmailEndpoint(FunctionalTest):
2649
     # -*- coding: utf-8 -*-
3023
     # -*- coding: utf-8 -*-
2650
     """
3024
     """
3024
             status=204,
3398
             status=204,
3025
         )
3399
         )
3026
         # Check After
3400
         # Check After
3401
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
3402
+        uapi = UserApi(
3403
+            current_user=admin,
3404
+            session=dbsession,
3405
+            config=self.app_config,
3406
+        )
3027
         user = uapi.get_one(user_id)
3407
         user = uapi.get_one(user_id)
3028
         assert not user.validate_password('pass')
3408
         assert not user.validate_password('pass')
3029
         assert user.validate_password('mynewpassword')
3409
         assert user.validate_password('mynewpassword')
3079
             params=params,
3459
             params=params,
3080
             status=403,
3460
             status=403,
3081
         )
3461
         )
3462
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
3463
+        uapi = UserApi(
3464
+            current_user=admin,
3465
+            session=dbsession,
3466
+            config=self.app_config,
3467
+        )
3082
         # Check After
3468
         # Check After
3083
         user = uapi.get_one(user_id)
3469
         user = uapi.get_one(user_id)
3084
         assert user.validate_password('pass')
3470
         assert user.validate_password('pass')
3137
             status=400,
3523
             status=400,
3138
         )
3524
         )
3139
         # Check After
3525
         # Check After
3526
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
3527
+        uapi = UserApi(
3528
+            current_user=admin,
3529
+            session=dbsession,
3530
+            config=self.app_config,
3531
+        )
3140
         user = uapi.get_one(user_id)
3532
         user = uapi.get_one(user_id)
3141
         assert user.validate_password('pass')
3533
         assert user.validate_password('pass')
3142
         assert not user.validate_password('mynewpassword')
3534
         assert not user.validate_password('mynewpassword')
3194
             status=204,
3586
             status=204,
3195
         )
3587
         )
3196
         # Check After
3588
         # Check After
3589
+        dbsession = get_tm_session(self.session_factory, transaction.manager)
3590
+        uapi = UserApi(
3591
+            current_user=admin,
3592
+            session=dbsession,
3593
+            config=self.app_config,
3594
+        )
3197
         user = uapi.get_one(user_id)
3595
         user = uapi.get_one(user_id)
3198
         assert not user.validate_password('pass')
3596
         assert not user.validate_password('pass')
3199
         assert user.validate_password('mynewpassword')
3597
         assert user.validate_password('mynewpassword')

+ 112 - 41
backend/tracim_backend/tests/functional/test_workspaces.py View File

2
 """
2
 """
3
 Tests for /api/v2/workspaces subpath endpoints.
3
 Tests for /api/v2/workspaces subpath endpoints.
4
 """
4
 """
5
-
5
+import requests
6
 import transaction
6
 import transaction
7
 from depot.io.utils import FileIntent
7
 from depot.io.utils import FileIntent
8
 
8
 
10
 from tracim_backend.lib.core.content import ContentApi
10
 from tracim_backend.lib.core.content import ContentApi
11
 from tracim_backend.lib.core.workspace import WorkspaceApi
11
 from tracim_backend.lib.core.workspace import WorkspaceApi
12
 from tracim_backend.models import get_tm_session
12
 from tracim_backend.models import get_tm_session
13
-from tracim_backend.models.data import ContentType
13
+from tracim_backend.models.contents import CONTENT_TYPES
14
 from tracim_backend.tests import FunctionalTest
14
 from tracim_backend.tests import FunctionalTest
15
 from tracim_backend.tests import set_html_document_slug_to_legacy
15
 from tracim_backend.tests import set_html_document_slug_to_legacy
16
 from tracim_backend.fixtures.content import Content as ContentFixtures
16
 from tracim_backend.fixtures.content import Content as ContentFixtures
41
         assert workspace['slug'] == 'business'
41
         assert workspace['slug'] == 'business'
42
         assert workspace['label'] == 'Business'
42
         assert workspace['label'] == 'Business'
43
         assert workspace['description'] == 'All importants documents'
43
         assert workspace['description'] == 'All importants documents'
44
-        assert len(workspace['sidebar_entries']) == 7
44
+        assert len(workspace['sidebar_entries']) == 5
45
 
45
 
46
+        # TODO - G.M - 2018-08-02 - Better test for sidebar entry, make it
47
+        # not fixed on active application/content-file
46
         sidebar_entry = workspace['sidebar_entries'][0]
48
         sidebar_entry = workspace['sidebar_entries'][0]
47
         assert sidebar_entry['slug'] == 'dashboard'
49
         assert sidebar_entry['slug'] == 'dashboard'
48
         assert sidebar_entry['label'] == 'Dashboard'
50
         assert sidebar_entry['label'] == 'Dashboard'
65
         assert sidebar_entry['fa_icon'] == "file-text-o"
67
         assert sidebar_entry['fa_icon'] == "file-text-o"
66
 
68
 
67
         sidebar_entry = workspace['sidebar_entries'][3]
69
         sidebar_entry = workspace['sidebar_entries'][3]
68
-        assert sidebar_entry['slug'] == 'contents/markdownpluspage'
69
-        assert sidebar_entry['label'] == 'Markdown Plus Documents'
70
-        assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=markdownpluspage"    # nopep8
71
-        assert sidebar_entry['hexcolor'] == "#f12d2d"
72
-        assert sidebar_entry['fa_icon'] == "file-code-o"
73
-
74
-        sidebar_entry = workspace['sidebar_entries'][4]
75
         assert sidebar_entry['slug'] == 'contents/file'
70
         assert sidebar_entry['slug'] == 'contents/file'
76
         assert sidebar_entry['label'] == 'Files'
71
         assert sidebar_entry['label'] == 'Files'
77
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=file"  # nopep8
72
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=file"  # nopep8
78
         assert sidebar_entry['hexcolor'] == "#FF9900"
73
         assert sidebar_entry['hexcolor'] == "#FF9900"
79
         assert sidebar_entry['fa_icon'] == "paperclip"
74
         assert sidebar_entry['fa_icon'] == "paperclip"
80
 
75
 
81
-        sidebar_entry = workspace['sidebar_entries'][5]
76
+        sidebar_entry = workspace['sidebar_entries'][4]
82
         assert sidebar_entry['slug'] == 'contents/thread'
77
         assert sidebar_entry['slug'] == 'contents/thread'
83
         assert sidebar_entry['label'] == 'Threads'
78
         assert sidebar_entry['label'] == 'Threads'
84
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=thread"  # nopep8
79
         assert sidebar_entry['route'] == "/#/workspaces/1/contents?type=thread"  # nopep8
85
         assert sidebar_entry['hexcolor'] == "#ad4cf9"
80
         assert sidebar_entry['hexcolor'] == "#ad4cf9"
86
         assert sidebar_entry['fa_icon'] == "comments-o"
81
         assert sidebar_entry['fa_icon'] == "comments-o"
87
 
82
 
88
-        sidebar_entry = workspace['sidebar_entries'][6]
89
-        assert sidebar_entry['slug'] == 'calendar'
90
-        assert sidebar_entry['label'] == 'Calendar'
91
-        assert sidebar_entry['route'] == "/#/workspaces/1/calendar"  # nopep8
92
-        assert sidebar_entry['hexcolor'] == "#757575"
93
-        assert sidebar_entry['fa_icon'] == "calendar"
94
-
95
     def test_api__update_workspace__ok_200__nominal_case(self) -> None:
83
     def test_api__update_workspace__ok_200__nominal_case(self) -> None:
96
         """
84
         """
97
         Test update workspace
85
         Test update workspace
118
         assert workspace['slug'] == 'business'
106
         assert workspace['slug'] == 'business'
119
         assert workspace['label'] == 'Business'
107
         assert workspace['label'] == 'Business'
120
         assert workspace['description'] == 'All importants documents'
108
         assert workspace['description'] == 'All importants documents'
121
-        assert len(workspace['sidebar_entries']) == 7
109
+        assert len(workspace['sidebar_entries']) == 5
122
 
110
 
123
         # modify workspace
111
         # modify workspace
124
         res = self.testapp.put_json(
112
         res = self.testapp.put_json(
132
         assert workspace['slug'] == 'superworkspace'
120
         assert workspace['slug'] == 'superworkspace'
133
         assert workspace['label'] == 'superworkspace'
121
         assert workspace['label'] == 'superworkspace'
134
         assert workspace['description'] == 'mysuperdescription'
122
         assert workspace['description'] == 'mysuperdescription'
135
-        assert len(workspace['sidebar_entries']) == 7
123
+        assert len(workspace['sidebar_entries']) == 5
136
 
124
 
137
         # after
125
         # after
138
         res = self.testapp.get(
126
         res = self.testapp.get(
145
         assert workspace['slug'] == 'superworkspace'
133
         assert workspace['slug'] == 'superworkspace'
146
         assert workspace['label'] == 'superworkspace'
134
         assert workspace['label'] == 'superworkspace'
147
         assert workspace['description'] == 'mysuperdescription'
135
         assert workspace['description'] == 'mysuperdescription'
148
-        assert len(workspace['sidebar_entries']) == 7
136
+        assert len(workspace['sidebar_entries']) == 5
149
 
137
 
150
     def test_api__update_workspace__err_400__empty_label(self) -> None:
138
     def test_api__update_workspace__err_400__empty_label(self) -> None:
151
         """
139
         """
612
         assert user_role['workspace_id'] == 1
600
         assert user_role['workspace_id'] == 1
613
 
601
 
614
 
602
 
603
+class TestUserInvitationWithMailActivatedSync(FunctionalTest):
604
+
605
+    fixtures = [BaseFixture, ContentFixtures]
606
+    config_section = 'functional_test_with_mail_test_sync'
607
+
608
+    def test_api__create_workspace_member_role__ok_200__new_user(self):  # nopep8
609
+        """
610
+        Create workspace member role
611
+        :return:
612
+        """
613
+        requests.delete('http://127.0.0.1:8025/api/v1/messages')
614
+        self.testapp.authorization = (
615
+            'Basic',
616
+            (
617
+                'admin@admin.admin',
618
+                'admin@admin.admin'
619
+            )
620
+        )
621
+        # create workspace role
622
+        params = {
623
+            'user_id': None,
624
+            'user_email_or_public_name': 'bob@bob.bob',
625
+            'role': 'content-manager',
626
+        }
627
+        res = self.testapp.post_json(
628
+            '/api/v2/workspaces/1/members',
629
+            status=200,
630
+            params=params,
631
+        )
632
+        user_role_found = res.json_body
633
+        assert user_role_found['role'] == 'content-manager'
634
+        assert user_role_found['user_id']
635
+        user_id = user_role_found['user_id']
636
+        assert user_role_found['workspace_id'] == 1
637
+        assert user_role_found['newly_created'] is True
638
+        assert user_role_found['email_sent'] is True
639
+
640
+        # check mail received
641
+        response = requests.get('http://127.0.0.1:8025/api/v1/messages')
642
+        response = response.json()
643
+        assert len(response) == 1
644
+        headers = response[0]['Content']['Headers']
645
+        assert headers['From'][0] == 'Tracim Notifications <test_user_from+0@localhost>'  # nopep8
646
+        assert headers['To'][0] == 'bob <bob@bob.bob>'
647
+        assert headers['Subject'][0] == '[TRACIM] Created account'
648
+
649
+        # TODO - G.M - 2018-08-02 - Place cleanup outside of the test
650
+        requests.delete('http://127.0.0.1:8025/api/v1/messages')
651
+
652
+
653
+class TestUserInvitationWithMailActivatedASync(FunctionalTest):
654
+
655
+    fixtures = [BaseFixture, ContentFixtures]
656
+    config_section = 'functional_test_with_mail_test_async'
657
+
658
+    def test_api__create_workspace_member_role__ok_200__new_user(self):  # nopep8
659
+        """
660
+        Create workspace member role
661
+        :return:
662
+        """
663
+        self.testapp.authorization = (
664
+            'Basic',
665
+            (
666
+                'admin@admin.admin',
667
+                'admin@admin.admin'
668
+            )
669
+        )
670
+        # create workspace role
671
+        params = {
672
+            'user_id': None,
673
+            'user_email_or_public_name': 'bob@bob.bob',
674
+            'role': 'content-manager',
675
+        }
676
+        res = self.testapp.post_json(
677
+            '/api/v2/workspaces/1/members',
678
+            status=200,
679
+            params=params,
680
+        )
681
+        user_role_found = res.json_body
682
+        assert user_role_found['newly_created'] is True
683
+        assert user_role_found['email_sent'] is False
684
+
685
+
615
 class TestWorkspaceContents(FunctionalTest):
686
 class TestWorkspaceContents(FunctionalTest):
616
     """
687
     """
617
     Tests for /api/v2/workspaces/{workspace_id}/contents endpoint
688
     Tests for /api/v2/workspaces/{workspace_id}/contents endpoint
983
             session=dbsession,
1054
             session=dbsession,
984
             config=self.app_config
1055
             config=self.app_config
985
         )
1056
         )
986
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1057
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
987
         test_thread = content_api.create(
1058
         test_thread = content_api.create(
988
-            content_type=ContentType.Thread,
1059
+            content_type_slug=CONTENT_TYPES.Thread.slug,
989
             workspace=business_workspace,
1060
             workspace=business_workspace,
990
             parent=tool_folder,
1061
             parent=tool_folder,
991
             label='Test Thread',
1062
             label='Test Thread',
995
         test_thread.description = 'Thread description'
1066
         test_thread.description = 'Thread description'
996
         dbsession.add(test_thread)
1067
         dbsession.add(test_thread)
997
         test_file = content_api.create(
1068
         test_file = content_api.create(
998
-            content_type=ContentType.File,
1069
+            content_type_slug=CONTENT_TYPES.File.slug,
999
             workspace=business_workspace,
1070
             workspace=business_workspace,
1000
             parent=tool_folder,
1071
             parent=tool_folder,
1001
             label='Test file',
1072
             label='Test file',
1009
             'text/plain',
1080
             'text/plain',
1010
         )
1081
         )
1011
         test_page_legacy = content_api.create(
1082
         test_page_legacy = content_api.create(
1012
-            content_type=ContentType.Page,
1083
+            content_type_slug=CONTENT_TYPES.Page.slug,
1013
             workspace=business_workspace,
1084
             workspace=business_workspace,
1014
             label='test_page',
1085
             label='test_page',
1015
             do_save=False,
1086
             do_save=False,
1016
             do_notify=False,
1087
             do_notify=False,
1017
         )
1088
         )
1018
-        test_page_legacy.type = ContentType.PageLegacy
1089
+        test_page_legacy.type = 'page'
1019
         content_api.update_content(test_page_legacy, 'test_page', '<p>PAGE</p>')
1090
         content_api.update_content(test_page_legacy, 'test_page', '<p>PAGE</p>')
1020
         test_html_document = content_api.create(
1091
         test_html_document = content_api.create(
1021
-            content_type=ContentType.Page,
1092
+            content_type_slug=CONTENT_TYPES.Page.slug,
1022
             workspace=business_workspace,
1093
             workspace=business_workspace,
1023
             label='test_html_page',
1094
             label='test_html_page',
1024
             do_save=False,
1095
             do_save=False,
1078
             session=dbsession,
1149
             session=dbsession,
1079
             config=self.app_config
1150
             config=self.app_config
1080
         )
1151
         )
1081
-        tool_folder = content_api.get_one(1, content_type=ContentType.Any)
1152
+        tool_folder = content_api.get_one(1, content_type=CONTENT_TYPES.Any_SLUG)
1082
         test_thread = content_api.create(
1153
         test_thread = content_api.create(
1083
-            content_type=ContentType.Thread,
1154
+            content_type_slug=CONTENT_TYPES.Thread.slug,
1084
             workspace=business_workspace,
1155
             workspace=business_workspace,
1085
             parent=tool_folder,
1156
             parent=tool_folder,
1086
             label='Test Thread',
1157
             label='Test Thread',
1090
         test_thread.description = 'Thread description'
1161
         test_thread.description = 'Thread description'
1091
         dbsession.add(test_thread)
1162
         dbsession.add(test_thread)
1092
         test_file = content_api.create(
1163
         test_file = content_api.create(
1093
-            content_type=ContentType.File,
1164
+            content_type_slug=CONTENT_TYPES.File.slug,
1094
             workspace=business_workspace,
1165
             workspace=business_workspace,
1095
             parent=tool_folder,
1166
             parent=tool_folder,
1096
             label='Test file',
1167
             label='Test file',
1104
             'text/plain',
1175
             'text/plain',
1105
         )
1176
         )
1106
         test_page_legacy = content_api.create(
1177
         test_page_legacy = content_api.create(
1107
-            content_type=ContentType.Page,
1178
+            content_type_slug=CONTENT_TYPES.Page.slug,
1108
             workspace=business_workspace,
1179
             workspace=business_workspace,
1109
             parent=tool_folder,
1180
             parent=tool_folder,
1110
             label='test_page',
1181
             label='test_page',
1111
             do_save=False,
1182
             do_save=False,
1112
             do_notify=False,
1183
             do_notify=False,
1113
         )
1184
         )
1114
-        test_page_legacy.type = ContentType.PageLegacy
1185
+        test_page_legacy.type = 'page'
1115
         content_api.update_content(test_page_legacy, 'test_page', '<p>PAGE</p>')
1186
         content_api.update_content(test_page_legacy, 'test_page', '<p>PAGE</p>')
1116
         test_html_document = content_api.create(
1187
         test_html_document = content_api.create(
1117
-            content_type=ContentType.Page,
1188
+            content_type_slug=CONTENT_TYPES.Page.slug,
1118
             workspace=business_workspace,
1189
             workspace=business_workspace,
1119
             parent=tool_folder,
1190
             parent=tool_folder,
1120
             label='test_html_page',
1191
             label='test_html_page',
1180
             'show_archived': 1,
1251
             'show_archived': 1,
1181
             'show_deleted': 1,
1252
             'show_deleted': 1,
1182
             'show_active': 1,
1253
             'show_active': 1,
1183
-            'content_type': 'any'
1254
+         #   'content_type': 'any'
1184
         }
1255
         }
1185
         self.testapp.authorization = (
1256
         self.testapp.authorization = (
1186
             'Basic',
1257
             'Basic',
1438
         params = {
1509
         params = {
1439
             'parent_id': None,
1510
             'parent_id': None,
1440
             'label': 'GenericCreatedContent',
1511
             'label': 'GenericCreatedContent',
1441
-            'content_type': 'markdownpage',
1512
+            'content_type': 'html-document',
1442
         }
1513
         }
1443
         res = self.testapp.post_json(
1514
         res = self.testapp.post_json(
1444
             '/api/v2/workspaces/1/contents',
1515
             '/api/v2/workspaces/1/contents',
1449
         assert res.json_body
1520
         assert res.json_body
1450
         assert res.json_body['status'] == 'open'
1521
         assert res.json_body['status'] == 'open'
1451
         assert res.json_body['content_id']
1522
         assert res.json_body['content_id']
1452
-        assert res.json_body['content_type'] == 'markdownpage'
1523
+        assert res.json_body['content_type'] == 'html-document'
1453
         assert res.json_body['is_archived'] is False
1524
         assert res.json_body['is_archived'] is False
1454
         assert res.json_body['is_deleted'] is False
1525
         assert res.json_body['is_deleted'] is False
1455
         assert res.json_body['workspace_id'] == 1
1526
         assert res.json_body['workspace_id'] == 1
1480
         )
1551
         )
1481
         params = {
1552
         params = {
1482
             'label': 'GenericCreatedContent',
1553
             'label': 'GenericCreatedContent',
1483
-            'content_type': 'markdownpage',
1554
+            'content_type': 'html-document',
1484
         }
1555
         }
1485
         res = self.testapp.post_json(
1556
         res = self.testapp.post_json(
1486
             '/api/v2/workspaces/1/contents',
1557
             '/api/v2/workspaces/1/contents',
1491
         assert res.json_body
1562
         assert res.json_body
1492
         assert res.json_body['status'] == 'open'
1563
         assert res.json_body['status'] == 'open'
1493
         assert res.json_body['content_id']
1564
         assert res.json_body['content_id']
1494
-        assert res.json_body['content_type'] == 'markdownpage'
1565
+        assert res.json_body['content_type'] == 'html-document'
1495
         assert res.json_body['is_archived'] is False
1566
         assert res.json_body['is_archived'] is False
1496
         assert res.json_body['is_deleted'] is False
1567
         assert res.json_body['is_deleted'] is False
1497
         assert res.json_body['workspace_id'] == 1
1568
         assert res.json_body['workspace_id'] == 1
1544
         )
1615
         )
1545
         params = {
1616
         params = {
1546
             'label': 'GenericCreatedContent',
1617
             'label': 'GenericCreatedContent',
1547
-            'content_type': 'markdownpage',
1618
+            'content_type': 'html-document',
1548
             'parent_id': 10,
1619
             'parent_id': 10,
1549
         }
1620
         }
1550
         res = self.testapp.post_json(
1621
         res = self.testapp.post_json(
1556
         assert res.json_body
1627
         assert res.json_body
1557
         assert res.json_body['status'] == 'open'
1628
         assert res.json_body['status'] == 'open'
1558
         assert res.json_body['content_id']
1629
         assert res.json_body['content_id']
1559
-        assert res.json_body['content_type'] == 'markdownpage'
1630
+        assert res.json_body['content_type'] == 'html-document'
1560
         assert res.json_body['is_archived'] is False
1631
         assert res.json_body['is_archived'] is False
1561
         assert res.json_body['is_deleted'] is False
1632
         assert res.json_body['is_deleted'] is False
1562
         assert res.json_body['workspace_id'] == 1
1633
         assert res.json_body['workspace_id'] == 1
1587
         )
1658
         )
1588
         params = {
1659
         params = {
1589
             'label': '',
1660
             'label': '',
1590
-            'content_type': 'markdownpage',
1661
+            'content_type': 'html-document',
1591
         }
1662
         }
1592
         res = self.testapp.post_json(
1663
         res = self.testapp.post_json(
1593
             '/api/v2/workspaces/1/contents',
1664
             '/api/v2/workspaces/1/contents',

+ 115 - 115
backend/tracim_backend/tests/library/test_content_api.py View File

14
 from tracim_backend.lib.core.workspace import RoleApi
14
 from tracim_backend.lib.core.workspace import RoleApi
15
 # TODO - G.M - 28-03-2018 - [WorkspaceApi] Re-enable WorkspaceApi
15
 # TODO - G.M - 28-03-2018 - [WorkspaceApi] Re-enable WorkspaceApi
16
 from tracim_backend.lib.core.workspace import WorkspaceApi
16
 from tracim_backend.lib.core.workspace import WorkspaceApi
17
+from tracim_backend.models.contents import CONTENT_TYPES
17
 from tracim_backend.models.revision_protection import new_revision
18
 from tracim_backend.models.revision_protection import new_revision
18
 from tracim_backend.models.auth import User
19
 from tracim_backend.models.auth import User
19
 from tracim_backend.models.auth import Group
20
 from tracim_backend.models.auth import Group
22
 from tracim_backend.models.data import ContentRevisionRO
23
 from tracim_backend.models.data import ContentRevisionRO
23
 from tracim_backend.models.data import Workspace
24
 from tracim_backend.models.data import Workspace
24
 from tracim_backend.models.data import Content
25
 from tracim_backend.models.data import Content
25
-from tracim_backend.models.data import ContentType
26
 from tracim_backend.models.data import UserRoleInWorkspace
26
 from tracim_backend.models.data import UserRoleInWorkspace
27
 from tracim_backend.fixtures.users_and_groups import Test as FixtureTest
27
 from tracim_backend.fixtures.users_and_groups import Test as FixtureTest
28
 from tracim_backend.tests import DefaultTest
28
 from tracim_backend.tests import DefaultTest
129
             config=self.app_config,
129
             config=self.app_config,
130
         )
130
         )
131
         item = api.create(
131
         item = api.create(
132
-            content_type=ContentType.Folder,
132
+            content_type_slug=CONTENT_TYPES.Folder.slug,
133
             workspace=workspace,
133
             workspace=workspace,
134
             parent=None,
134
             parent=None,
135
             label='not_deleted',
135
             label='not_deleted',
136
             do_save=True
136
             do_save=True
137
         )
137
         )
138
         item2 = api.create(
138
         item2 = api.create(
139
-            content_type=ContentType.Folder,
139
+            content_type_slug=CONTENT_TYPES.Folder.slug,
140
             workspace=workspace,
140
             workspace=workspace,
141
             parent=None,
141
             parent=None,
142
             label='to_delete',
142
             label='to_delete',
159
             session=self.session,
159
             session=self.session,
160
             config=self.app_config,
160
             config=self.app_config,
161
         )
161
         )
162
-        items = api.get_all(None, ContentType.Any, workspace)
162
+        items = api.get_all(None, CONTENT_TYPES.Any_SLUG, workspace)
163
         eq_(2, len(items))
163
         eq_(2, len(items))
164
 
164
 
165
-        items = api.get_all(None, ContentType.Any, workspace)
165
+        items = api.get_all(None, CONTENT_TYPES.Any_SLUG, workspace)
166
         with new_revision(
166
         with new_revision(
167
                 session=self.session,
167
                 session=self.session,
168
                 tm=transaction.manager,
168
                 tm=transaction.manager,
184
             session=self.session,
184
             session=self.session,
185
             config=self.app_config,
185
             config=self.app_config,
186
         )
186
         )
187
-        items = api.get_all(None, ContentType.Any, workspace)
187
+        items = api.get_all(None, CONTENT_TYPES.Any_SLUG, workspace)
188
         eq_(1, len(items))
188
         eq_(1, len(items))
189
         transaction.commit()
189
         transaction.commit()
190
 
190
 
202
             config=self.app_config,
202
             config=self.app_config,
203
             show_deleted=True,
203
             show_deleted=True,
204
         )
204
         )
205
-        items = api.get_all(None, ContentType.Any, workspace)
205
+        items = api.get_all(None, CONTENT_TYPES.Any_SLUG, workspace)
206
         eq_(2, len(items))
206
         eq_(2, len(items))
207
 
207
 
208
     def test_archive(self):
208
     def test_archive(self):
240
             config=self.app_config,
240
             config=self.app_config,
241
         )
241
         )
242
         item = api.create(
242
         item = api.create(
243
-            content_type=ContentType.Folder,
243
+            content_type_slug=CONTENT_TYPES.Folder.slug,
244
             workspace=workspace,
244
             workspace=workspace,
245
             parent=None,
245
             parent=None,
246
             label='not_archived',
246
             label='not_archived',
247
             do_save=True
247
             do_save=True
248
         )
248
         )
249
         item2 = api.create(
249
         item2 = api.create(
250
-            content_type=ContentType.Folder,
250
+            content_type_slug=CONTENT_TYPES.Folder.slug,
251
             workspace=workspace,
251
             workspace=workspace,
252
             parent=None,
252
             parent=None,
253
             label='to_archive',
253
             label='to_archive',
269
             config=self.app_config,
269
             config=self.app_config,
270
         )
270
         )
271
 
271
 
272
-        items = api.get_all(None, ContentType.Any, workspace)
272
+        items = api.get_all(None, CONTENT_TYPES.Any_SLUG, workspace)
273
         eq_(2, len(items))
273
         eq_(2, len(items))
274
 
274
 
275
-        items = api.get_all(None, ContentType.Any, workspace)
275
+        items = api.get_all(None, CONTENT_TYPES.Any_SLUG, workspace)
276
         with new_revision(
276
         with new_revision(
277
                 session=self.session,
277
                 session=self.session,
278
                 tm=transaction.manager,
278
                 tm=transaction.manager,
295
             config=self.app_config,
295
             config=self.app_config,
296
         )
296
         )
297
 
297
 
298
-        items = api.get_all(None, ContentType.Any, workspace)
298
+        items = api.get_all(None, CONTENT_TYPES.Any_SLUG, workspace)
299
         eq_(1, len(items))
299
         eq_(1, len(items))
300
         transaction.commit()
300
         transaction.commit()
301
 
301
 
320
             config=self.app_config,
320
             config=self.app_config,
321
             show_archived=True,
321
             show_archived=True,
322
         )
322
         )
323
-        items = api.get_all(None, ContentType.Any, workspace)
323
+        items = api.get_all(None, CONTENT_TYPES.Any_SLUG, workspace)
324
         eq_(2, len(items))
324
         eq_(2, len(items))
325
 
325
 
326
     def test_get_all_with_filter(self):
326
     def test_get_all_with_filter(self):
358
             config=self.app_config,
358
             config=self.app_config,
359
         )
359
         )
360
         item = api.create(
360
         item = api.create(
361
-            content_type=ContentType.Folder,
361
+            content_type_slug=CONTENT_TYPES.Folder.slug,
362
             workspace=workspace,
362
             workspace=workspace,
363
             parent=None,
363
             parent=None,
364
             label='thefolder',
364
             label='thefolder',
365
             do_save=True
365
             do_save=True
366
         )
366
         )
367
         item2 = api.create(
367
         item2 = api.create(
368
-            content_type=ContentType.File,
368
+            content_type_slug=CONTENT_TYPES.File.slug,
369
             workspace=workspace,
369
             workspace=workspace,
370
             parent=None,
370
             parent=None,
371
             label='thefile',
371
             label='thefile',
389
             config=self.app_config,
389
             config=self.app_config,
390
         )
390
         )
391
 
391
 
392
-        items = api.get_all(None, ContentType.Any, workspace)
392
+        items = api.get_all(None, CONTENT_TYPES.Any_SLUG, workspace)
393
         eq_(2, len(items))
393
         eq_(2, len(items))
394
 
394
 
395
-        items2 = api.get_all(None, ContentType.File, workspace)
395
+        items2 = api.get_all(None, CONTENT_TYPES.File.slug, workspace)
396
         eq_(1, len(items2))
396
         eq_(1, len(items2))
397
         eq_('thefile', items2[0].label)
397
         eq_('thefile', items2[0].label)
398
 
398
 
399
-        items3 = api.get_all(None, ContentType.Folder, workspace)
399
+        items3 = api.get_all(None, CONTENT_TYPES.Folder.slug, workspace)
400
         eq_(1, len(items3))
400
         eq_(1, len(items3))
401
         eq_('thefolder', items3[0].label)
401
         eq_('thefolder', items3[0].label)
402
 
402
 
428
             config=self.app_config,
428
             config=self.app_config,
429
         )
429
         )
430
         item = api.create(
430
         item = api.create(
431
-            ContentType.Folder,
431
+            CONTENT_TYPES.Folder.slug,
432
             workspace,
432
             workspace,
433
             None,
433
             None,
434
             'parent',
434
             'parent',
435
             do_save=True,
435
             do_save=True,
436
         )
436
         )
437
         item2 = api.create(
437
         item2 = api.create(
438
-            ContentType.File,
438
+            CONTENT_TYPES.File.slug,
439
             workspace,
439
             workspace,
440
             item,
440
             item,
441
             'file1',
441
             'file1',
442
             do_save=True,
442
             do_save=True,
443
         )
443
         )
444
         item3 = api.create(
444
         item3 = api.create(
445
-            ContentType.File,
445
+            CONTENT_TYPES.File.slug,
446
             workspace,
446
             workspace,
447
             None,
447
             None,
448
             'file2',
448
             'file2',
468
             config=self.app_config,
468
             config=self.app_config,
469
         )
469
         )
470
 
470
 
471
-        items = api.get_all(None, ContentType.Any, workspace)
471
+        items = api.get_all(None, CONTENT_TYPES.Any_SLUG, workspace)
472
         eq_(3, len(items))
472
         eq_(3, len(items))
473
 
473
 
474
-        items2 = api.get_all(parent_id, ContentType.File, workspace)
474
+        items2 = api.get_all(parent_id, CONTENT_TYPES.File.slug, workspace)
475
         eq_(1, len(items2))
475
         eq_(1, len(items2))
476
         eq_(child_id, items2[0].content_id)
476
         eq_(child_id, items2[0].content_id)
477
 
477
 
506
             session=self.session,
506
             session=self.session,
507
             config=self.app_config,
507
             config=self.app_config,
508
         )
508
         )
509
-        c = api.create(ContentType.Folder, workspace, None, 'parent', '', True)
509
+        c = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'parent', '', True)
510
         with new_revision(
510
         with new_revision(
511
             session=self.session,
511
             session=self.session,
512
             tm=transaction.manager,
512
             tm=transaction.manager,
546
             session=self.session,
546
             session=self.session,
547
             config=self.app_config,
547
             config=self.app_config,
548
         )
548
         )
549
-        c = api.create(ContentType.Folder, workspace, None, 'parent', '', True)
549
+        c = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'parent', '', True)
550
         with new_revision(
550
         with new_revision(
551
             session=self.session,
551
             session=self.session,
552
             tm=transaction.manager,
552
             tm=transaction.manager,
591
             session=self.session,
591
             session=self.session,
592
             config=self.app_config,
592
             config=self.app_config,
593
         )
593
         )
594
-        p = api.create(ContentType.Page, workspace, None, 'this_is_a_page')
594
+        p = api.create(CONTENT_TYPES.Page.slug, workspace, None, 'this_is_a_page')
595
         c = api.create_comment(workspace, p, 'this is the comment', True)
595
         c = api.create_comment(workspace, p, 'this is the comment', True)
596
 
596
 
597
         eq_(Content, c.__class__)
597
         eq_(Content, c.__class__)
598
         eq_(p.content_id, c.parent_id)
598
         eq_(p.content_id, c.parent_id)
599
         eq_(user, c.owner)
599
         eq_(user, c.owner)
600
         eq_(workspace, c.workspace)
600
         eq_(workspace, c.workspace)
601
-        eq_(ContentType.Comment, c.type)
601
+        eq_(CONTENT_TYPES.Comment.slug, c.type)
602
         eq_('this is the comment', c.description)
602
         eq_('this is the comment', c.description)
603
         eq_('', c.label)
603
         eq_('', c.label)
604
         eq_(ActionDescription.COMMENT, c.revision_type)
604
         eq_(ActionDescription.COMMENT, c.revision_type)
652
             config=self.app_config,
652
             config=self.app_config,
653
         )
653
         )
654
         foldera = api.create(
654
         foldera = api.create(
655
-            ContentType.Folder,
655
+            CONTENT_TYPES.Folder.slug,
656
             workspace,
656
             workspace,
657
             None,
657
             None,
658
             'folder a',
658
             'folder a',
661
         )
661
         )
662
         with self.session.no_autoflush:
662
         with self.session.no_autoflush:
663
             text_file = api.create(
663
             text_file = api.create(
664
-                content_type=ContentType.File,
664
+                content_type_slug=CONTENT_TYPES.File.slug,
665
                 workspace=workspace,
665
                 workspace=workspace,
666
                 parent=foldera,
666
                 parent=foldera,
667
                 label='test_file',
667
                 label='test_file',
689
             save_now=True
689
             save_now=True
690
         )
690
         )
691
         folderb = api2.create(
691
         folderb = api2.create(
692
-            ContentType.Folder,
692
+            CONTENT_TYPES.Folder.slug,
693
             workspace2,
693
             workspace2,
694
             None,
694
             None,
695
             'folder b',
695
             'folder b',
773
             config=self.app_config,
773
             config=self.app_config,
774
         )
774
         )
775
         foldera = api.create(
775
         foldera = api.create(
776
-            ContentType.Folder,
776
+            CONTENT_TYPES.Folder.slug,
777
             workspace,
777
             workspace,
778
             None,
778
             None,
779
             'folder a',
779
             'folder a',
782
         )
782
         )
783
         with self.session.no_autoflush:
783
         with self.session.no_autoflush:
784
             text_file = api.create(
784
             text_file = api.create(
785
-                content_type=ContentType.File,
785
+                content_type_slug=CONTENT_TYPES.File.slug,
786
                 workspace=workspace,
786
                 workspace=workspace,
787
                 parent=foldera,
787
                 parent=foldera,
788
                 label='test_file',
788
                 label='test_file',
810
             save_now=True
810
             save_now=True
811
         )
811
         )
812
         folderb = api2.create(
812
         folderb = api2.create(
813
-            ContentType.Folder,
813
+            CONTENT_TYPES.Folder.slug,
814
             workspace2,
814
             workspace2,
815
             None,
815
             None,
816
             'folder b',
816
             'folder b',
891
             config=self.app_config,
891
             config=self.app_config,
892
         )
892
         )
893
         foldera = api.create(
893
         foldera = api.create(
894
-            ContentType.Folder,
894
+            CONTENT_TYPES.Folder.slug,
895
             workspace,
895
             workspace,
896
             None,
896
             None,
897
             'folder a',
897
             'folder a',
900
         )
900
         )
901
         with self.session.no_autoflush:
901
         with self.session.no_autoflush:
902
             text_file = api.create(
902
             text_file = api.create(
903
-                content_type=ContentType.File,
903
+                content_type_slug=CONTENT_TYPES.File.slug,
904
                 workspace=workspace,
904
                 workspace=workspace,
905
                 parent=foldera,
905
                 parent=foldera,
906
                 label='test_file',
906
                 label='test_file',
1014
 
1014
 
1015
         # Creates page_1 & page_2 in workspace 1
1015
         # Creates page_1 & page_2 in workspace 1
1016
         #     and page_3 & page_4 in workspace 2
1016
         #     and page_3 & page_4 in workspace 2
1017
-        page_1 = cont_api_a.create(ContentType.Page, workspace1, None,
1017
+        page_1 = cont_api_a.create(CONTENT_TYPES.Page.slug, workspace1, None,
1018
                                    'this is a page', do_save=True)
1018
                                    'this is a page', do_save=True)
1019
-        page_2 = cont_api_a.create(ContentType.Page, workspace1, None,
1019
+        page_2 = cont_api_a.create(CONTENT_TYPES.Page.slug, workspace1, None,
1020
                                    'this is page1', do_save=True)
1020
                                    'this is page1', do_save=True)
1021
-        page_3 = cont_api_a.create(ContentType.Thread, workspace2, None,
1021
+        page_3 = cont_api_a.create(CONTENT_TYPES.Thread.slug, workspace2, None,
1022
                                    'this is page2', do_save=True)
1022
                                    'this is page2', do_save=True)
1023
-        page_4 = cont_api_a.create(ContentType.File, workspace2, None,
1023
+        page_4 = cont_api_a.create(CONTENT_TYPES.File.slug, workspace2, None,
1024
                                    'this is page3', do_save=True)
1024
                                    'this is page3', do_save=True)
1025
 
1025
 
1026
         for rev in page_1.revisions:
1026
         for rev in page_1.revisions:
1118
             config=self.app_config,
1118
             config=self.app_config,
1119
         )
1119
         )
1120
 
1120
 
1121
-        page_1 = cont_api_a.create(ContentType.Page, workspace, None,
1121
+        page_1 = cont_api_a.create(CONTENT_TYPES.Page.slug, workspace, None,
1122
                                    'this is a page', do_save=True)
1122
                                    'this is a page', do_save=True)
1123
 
1123
 
1124
         for rev in page_1.revisions:
1124
         for rev in page_1.revisions:
1187
         )
1187
         )
1188
 
1188
 
1189
         page_2 = cont_api_a.create(
1189
         page_2 = cont_api_a.create(
1190
-            ContentType.Page,
1190
+            CONTENT_TYPES.Page.slug,
1191
             workspace,
1191
             workspace,
1192
             None,
1192
             None,
1193
             'this is page1',
1193
             'this is page1',
1194
             do_save=True
1194
             do_save=True
1195
         )
1195
         )
1196
         page_3 = cont_api_a.create(
1196
         page_3 = cont_api_a.create(
1197
-            ContentType.Thread,
1197
+            CONTENT_TYPES.Thread.slug,
1198
             workspace,
1198
             workspace,
1199
             None,
1199
             None,
1200
             'this is page2',
1200
             'this is page2',
1201
             do_save=True
1201
             do_save=True
1202
         )
1202
         )
1203
         page_4 = cont_api_a.create(
1203
         page_4 = cont_api_a.create(
1204
-            ContentType.File,
1204
+            CONTENT_TYPES.File.slug,
1205
             workspace,
1205
             workspace,
1206
             None,
1206
             None,
1207
             'this is page3',
1207
             'this is page3',
1285
         )
1285
         )
1286
 
1286
 
1287
         p = api.create(
1287
         p = api.create(
1288
-            content_type=ContentType.Page,
1288
+            content_type_slug=CONTENT_TYPES.Page.slug,
1289
             workspace=workspace,
1289
             workspace=workspace,
1290
             parent=None,
1290
             parent=None,
1291
             label='this_is_a_page',
1291
             label='this_is_a_page',
1312
             config=self.app_config,
1312
             config=self.app_config,
1313
         )
1313
         )
1314
 
1314
 
1315
-        content = api.get_one(pcid, ContentType.Any, workspace)
1315
+        content = api.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1316
         eq_(u1id, content.owner_id)
1316
         eq_(u1id, content.owner_id)
1317
         eq_(poid, content.owner_id)
1317
         eq_(poid, content.owner_id)
1318
 
1318
 
1326
             session=self.session,
1326
             session=self.session,
1327
             config=self.app_config,
1327
             config=self.app_config,
1328
         )
1328
         )
1329
-        content2 = api2.get_one(pcid, ContentType.Any, workspace)
1329
+        content2 = api2.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1330
         with new_revision(
1330
         with new_revision(
1331
            session=self.session,
1331
            session=self.session,
1332
            tm=transaction.manager,
1332
            tm=transaction.manager,
1353
             config=self.app_config,
1353
             config=self.app_config,
1354
         )
1354
         )
1355
 
1355
 
1356
-        updated = api.get_one(pcid, ContentType.Any, workspace)
1356
+        updated = api.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1357
         eq_(u2id, updated.owner_id,
1357
         eq_(u2id, updated.owner_id,
1358
             'the owner id should be {} (found {})'.format(u2id,
1358
             'the owner id should be {} (found {})'.format(u2id,
1359
                                                           updated.owner_id))
1359
                                                           updated.owner_id))
1412
         )
1412
         )
1413
         with self.session.no_autoflush:
1413
         with self.session.no_autoflush:
1414
             page = api.create(
1414
             page = api.create(
1415
-                content_type=ContentType.Page,
1415
+                content_type_slug=CONTENT_TYPES.Page.slug,
1416
                 workspace=workspace,
1416
                 workspace=workspace,
1417
                 label="same_content",
1417
                 label="same_content",
1418
                 do_save=False
1418
                 do_save=False
1426
             session=self.session,
1426
             session=self.session,
1427
             config=self.app_config,
1427
             config=self.app_config,
1428
         )
1428
         )
1429
-        content2 = api2.get_one(page.content_id, ContentType.Any, workspace)
1429
+        content2 = api2.get_one(page.content_id, CONTENT_TYPES.Any_SLUG, workspace)
1430
         with new_revision(
1430
         with new_revision(
1431
            session=self.session,
1431
            session=self.session,
1432
            tm=transaction.manager,
1432
            tm=transaction.manager,
1495
             config=self.app_config,
1495
             config=self.app_config,
1496
         )
1496
         )
1497
         p = api.create(
1497
         p = api.create(
1498
-            content_type=ContentType.File,
1498
+            content_type_slug=CONTENT_TYPES.File.slug,
1499
             workspace=workspace,
1499
             workspace=workspace,
1500
             parent=None,
1500
             parent=None,
1501
             label='this_is_a_page',
1501
             label='this_is_a_page',
1524
             config=self.app_config,
1524
             config=self.app_config,
1525
         )
1525
         )
1526
 
1526
 
1527
-        content = api.get_one(pcid, ContentType.Any, workspace)
1527
+        content = api.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1528
         eq_(u1id, content.owner_id)
1528
         eq_(u1id, content.owner_id)
1529
         eq_(poid, content.owner_id)
1529
         eq_(poid, content.owner_id)
1530
 
1530
 
1538
             session=self.session,
1538
             session=self.session,
1539
             config=self.app_config,
1539
             config=self.app_config,
1540
         )
1540
         )
1541
-        content2 = api2.get_one(pcid, ContentType.Any, workspace)
1541
+        content2 = api2.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1542
         with new_revision(
1542
         with new_revision(
1543
             session=self.session,
1543
             session=self.session,
1544
             tm=transaction.manager,
1544
             tm=transaction.manager,
1561
             config=self.app_config,
1561
             config=self.app_config,
1562
         ).get_one(wid)
1562
         ).get_one(wid)
1563
 
1563
 
1564
-        updated = api.get_one(pcid, ContentType.Any, workspace)
1564
+        updated = api.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1565
         eq_(u2id, updated.owner_id,
1565
         eq_(u2id, updated.owner_id,
1566
             'the owner id should be {} (found {})'.format(u2id,
1566
             'the owner id should be {} (found {})'.format(u2id,
1567
                                                           updated.owner_id))
1567
                                                           updated.owner_id))
1622
         )
1622
         )
1623
         with self.session.no_autoflush:
1623
         with self.session.no_autoflush:
1624
             page = api.create(
1624
             page = api.create(
1625
-                content_type=ContentType.Page,
1625
+                content_type_slug=CONTENT_TYPES.Page.slug,
1626
                 workspace=workspace,
1626
                 workspace=workspace,
1627
                 label="same_content",
1627
                 label="same_content",
1628
                 do_save=False
1628
                 do_save=False
1641
             session=self.session,
1641
             session=self.session,
1642
             config=self.app_config,
1642
             config=self.app_config,
1643
         )
1643
         )
1644
-        content2 = api2.get_one(page.content_id, ContentType.Any, workspace)
1644
+        content2 = api2.get_one(page.content_id, CONTENT_TYPES.Any_SLUG, workspace)
1645
         with new_revision(
1645
         with new_revision(
1646
             session=self.session,
1646
             session=self.session,
1647
             tm=transaction.manager,
1647
             tm=transaction.manager,
1713
             config=self.app_config,
1713
             config=self.app_config,
1714
         )
1714
         )
1715
         p = api.create(
1715
         p = api.create(
1716
-            content_type=ContentType.File,
1716
+            content_type_slug=CONTENT_TYPES.File.slug,
1717
             workspace=workspace,
1717
             workspace=workspace,
1718
             parent=None,
1718
             parent=None,
1719
             label='this_is_a_page',
1719
             label='this_is_a_page',
1741
             config=self.app_config,
1741
             config=self.app_config,
1742
         ).get_one(wid)
1742
         ).get_one(wid)
1743
 
1743
 
1744
-        content = api.get_one(pcid, ContentType.Any, workspace)
1744
+        content = api.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1745
         eq_(u1id, content.owner_id)
1745
         eq_(u1id, content.owner_id)
1746
         eq_(poid, content.owner_id)
1746
         eq_(poid, content.owner_id)
1747
 
1747
 
1757
             config=self.app_config,
1757
             config=self.app_config,
1758
             show_archived=True,
1758
             show_archived=True,
1759
         )
1759
         )
1760
-        content2 = api2.get_one(pcid, ContentType.Any, workspace)
1760
+        content2 = api2.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1761
         with new_revision(
1761
         with new_revision(
1762
                 session=self.session,
1762
                 session=self.session,
1763
                 tm=transaction.manager,
1763
                 tm=transaction.manager,
1796
             show_archived=True,
1796
             show_archived=True,
1797
         )
1797
         )
1798
 
1798
 
1799
-        updated = api2.get_one(pcid, ContentType.Any, workspace)
1799
+        updated = api2.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1800
         eq_(u2id, updated.owner_id,
1800
         eq_(u2id, updated.owner_id,
1801
             'the owner id should be {} (found {})'.format(u2id,
1801
             'the owner id should be {} (found {})'.format(u2id,
1802
                                                           updated.owner_id))
1802
                                                           updated.owner_id))
1805
 
1805
 
1806
         ####
1806
         ####
1807
 
1807
 
1808
-        updated2 = api.get_one(pcid, ContentType.Any, workspace)
1808
+        updated2 = api.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1809
         with new_revision(
1809
         with new_revision(
1810
             session=self.session,
1810
             session=self.session,
1811
             tm=transaction.manager,
1811
             tm=transaction.manager,
1874
             show_deleted=True,
1874
             show_deleted=True,
1875
         )
1875
         )
1876
         p = api.create(
1876
         p = api.create(
1877
-            content_type=ContentType.File,
1877
+            content_type_slug=CONTENT_TYPES.File.slug,
1878
             workspace=workspace,
1878
             workspace=workspace,
1879
             parent=None,
1879
             parent=None,
1880
             label='this_is_a_page',
1880
             label='this_is_a_page',
1900
             config=self.app_config,
1900
             config=self.app_config,
1901
         ).get_one(wid)
1901
         ).get_one(wid)
1902
 
1902
 
1903
-        content = api.get_one(pcid, ContentType.Any, workspace)
1903
+        content = api.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1904
         eq_(u1id, content.owner_id)
1904
         eq_(u1id, content.owner_id)
1905
         eq_(poid, content.owner_id)
1905
         eq_(poid, content.owner_id)
1906
 
1906
 
1915
             config=self.app_config,
1915
             config=self.app_config,
1916
             show_deleted=True,
1916
             show_deleted=True,
1917
         )
1917
         )
1918
-        content2 = api2.get_one(pcid, ContentType.Any, workspace)
1918
+        content2 = api2.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1919
         with new_revision(
1919
         with new_revision(
1920
                 session=self.session,
1920
                 session=self.session,
1921
                 tm=transaction.manager,
1921
                 tm=transaction.manager,
1956
             show_deleted=True
1956
             show_deleted=True
1957
         )
1957
         )
1958
 
1958
 
1959
-        updated = api2.get_one(pcid, ContentType.Any, workspace)
1959
+        updated = api2.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1960
         eq_(u2id, updated.owner_id,
1960
         eq_(u2id, updated.owner_id,
1961
             'the owner id should be {} (found {})'.format(u2id,
1961
             'the owner id should be {} (found {})'.format(u2id,
1962
                                                           updated.owner_id))
1962
                                                           updated.owner_id))
1965
 
1965
 
1966
         ####
1966
         ####
1967
 
1967
 
1968
-        updated2 = api.get_one(pcid, ContentType.Any, workspace)
1968
+        updated2 = api.get_one(pcid, CONTENT_TYPES.Any_SLUG, workspace)
1969
         with new_revision(
1969
         with new_revision(
1970
             tm=transaction.manager,
1970
             tm=transaction.manager,
1971
             session=self.session,
1971
             session=self.session,
2016
             session=self.session,
2016
             session=self.session,
2017
             config=self.app_config,
2017
             config=self.app_config,
2018
         )
2018
         )
2019
-        main_folder_workspace2 = api.create(ContentType.Folder, workspace2, None, 'Hepla', '', True)  # nopep8
2020
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
2019
+        main_folder_workspace2 = api.create(CONTENT_TYPES.Folder.slug, workspace2, None, 'Hepla', '', True)  # nopep8
2020
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
2021
         # creation order test
2021
         # creation order test
2022
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2023
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
2022
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2023
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
2024
         # update order test
2024
         # update order test
2025
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
2026
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
2025
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
2026
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
2027
         with new_revision(
2027
         with new_revision(
2028
             session=self.session,
2028
             session=self.session,
2029
             tm=transaction.manager,
2029
             tm=transaction.manager,
2032
             firstly_created_but_recently_updated.description = 'Just an update'
2032
             firstly_created_but_recently_updated.description = 'Just an update'
2033
         api.save(firstly_created_but_recently_updated)
2033
         api.save(firstly_created_but_recently_updated)
2034
         # comment change order
2034
         # comment change order
2035
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
2036
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
2035
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
2036
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
2037
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
2037
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
2038
 
2038
 
2039
-        content_workspace_2 = api.create(ContentType.Page, workspace2 ,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
2039
+        content_workspace_2 = api.create(CONTENT_TYPES.Page.slug, workspace2 ,main_folder_workspace2, 'content_workspace_2', '',True)  # nopep8
2040
         last_actives = api.get_last_active()
2040
         last_actives = api.get_last_active()
2041
         assert len(last_actives) == 9
2041
         assert len(last_actives) == 9
2042
         # workspace_2 content
2042
         # workspace_2 content
2088
             session=self.session,
2088
             session=self.session,
2089
             config=self.app_config,
2089
             config=self.app_config,
2090
         )
2090
         )
2091
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
2091
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
2092
         # creation order test
2092
         # creation order test
2093
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2094
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
2093
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2094
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
2095
         # update order test
2095
         # update order test
2096
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
2097
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
2096
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
2097
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
2098
         with new_revision(
2098
         with new_revision(
2099
             session=self.session,
2099
             session=self.session,
2100
             tm=transaction.manager,
2100
             tm=transaction.manager,
2103
             firstly_created_but_recently_updated.description = 'Just an update'
2103
             firstly_created_but_recently_updated.description = 'Just an update'
2104
         api.save(firstly_created_but_recently_updated)
2104
         api.save(firstly_created_but_recently_updated)
2105
         # comment change order
2105
         # comment change order
2106
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
2107
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
2106
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
2107
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
2108
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
2108
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
2109
 
2109
 
2110
         last_actives = api.get_last_active(workspace=workspace)
2110
         last_actives = api.get_last_active(workspace=workspace)
2153
             session=self.session,
2153
             session=self.session,
2154
             config=self.app_config,
2154
             config=self.app_config,
2155
         )
2155
         )
2156
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
2156
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
2157
         # creation order test
2157
         # creation order test
2158
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2159
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
2158
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2159
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
2160
         # update order test
2160
         # update order test
2161
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
2162
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
2161
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
2162
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
2163
         with new_revision(
2163
         with new_revision(
2164
             session=self.session,
2164
             session=self.session,
2165
             tm=transaction.manager,
2165
             tm=transaction.manager,
2168
             firstly_created_but_recently_updated.description = 'Just an update'
2168
             firstly_created_but_recently_updated.description = 'Just an update'
2169
         api.save(firstly_created_but_recently_updated)
2169
         api.save(firstly_created_but_recently_updated)
2170
         # comment change order
2170
         # comment change order
2171
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
2172
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
2171
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
2172
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
2173
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
2173
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
2174
 
2174
 
2175
         selected_contents = [
2175
         selected_contents = [
2228
             session=self.session,
2228
             session=self.session,
2229
             config=self.app_config,
2229
             config=self.app_config,
2230
         )
2230
         )
2231
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
2231
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
2232
         # creation order test
2232
         # creation order test
2233
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2234
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
2233
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2234
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
2235
         # update order test
2235
         # update order test
2236
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
2237
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
2236
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
2237
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
2238
         with new_revision(
2238
         with new_revision(
2239
             session=self.session,
2239
             session=self.session,
2240
             tm=transaction.manager,
2240
             tm=transaction.manager,
2243
             firstly_created_but_recently_updated.description = 'Just an update'
2243
             firstly_created_but_recently_updated.description = 'Just an update'
2244
         api.save(firstly_created_but_recently_updated)
2244
         api.save(firstly_created_but_recently_updated)
2245
         # comment change order
2245
         # comment change order
2246
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
2247
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
2246
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
2247
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
2248
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
2248
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
2249
 
2249
 
2250
         last_actives = api.get_last_active(workspace=workspace, limit=2)  # nopep8
2250
         last_actives = api.get_last_active(workspace=workspace, limit=2)  # nopep8
2309
             session=self.session,
2309
             session=self.session,
2310
             config=self.app_config,
2310
             config=self.app_config,
2311
         )
2311
         )
2312
-        main_folder = api.create(ContentType.Folder, workspace, None, 'this is randomized folder', '', True)  # nopep8
2312
+        main_folder = api.create(CONTENT_TYPES.Folder.slug, workspace, None, 'this is randomized folder', '', True)  # nopep8
2313
         # creation order test
2313
         # creation order test
2314
-        firstly_created = api.create(ContentType.Page, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2315
-        secondly_created = api.create(ContentType.Page, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
2314
+        firstly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'creation_order_test', '', True)  # nopep8
2315
+        secondly_created = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another creation_order_test', '', True)  # nopep8
2316
         # update order test
2316
         # update order test
2317
-        firstly_created_but_recently_updated = api.create(ContentType.Page, workspace, main_folder, 'update_order_test', '', True)  # nopep8
2318
-        secondly_created_but_not_updated = api.create(ContentType.Page, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
2317
+        firstly_created_but_recently_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'update_order_test', '', True)  # nopep8
2318
+        secondly_created_but_not_updated = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'another update_order_test', '', True)  # nopep8
2319
         with new_revision(
2319
         with new_revision(
2320
             session=self.session,
2320
             session=self.session,
2321
             tm=transaction.manager,
2321
             tm=transaction.manager,
2324
             firstly_created_but_recently_updated.description = 'Just an update'
2324
             firstly_created_but_recently_updated.description = 'Just an update'
2325
         api.save(firstly_created_but_recently_updated)
2325
         api.save(firstly_created_but_recently_updated)
2326
         # comment change order
2326
         # comment change order
2327
-        firstly_created_but_recently_commented = api.create(ContentType.Page, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
2328
-        secondly_created_but_not_commented = api.create(ContentType.Page, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
2327
+        firstly_created_but_recently_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is randomized label content', '', True)  # nopep8
2328
+        secondly_created_but_not_commented = api.create(CONTENT_TYPES.Page.slug, workspace, main_folder, 'this is another randomized label content', '', True)  # nopep8
2329
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
2329
         comments = api.create_comment(workspace, firstly_created_but_recently_commented, 'juste a super comment', True)  # nopep8
2330
 
2330
 
2331
         last_actives = api.get_last_active(workspace=workspace2)
2331
         last_actives = api.get_last_active(workspace=workspace2)
2366
             session=self.session,
2366
             session=self.session,
2367
             config=self.app_config,
2367
             config=self.app_config,
2368
         )
2368
         )
2369
-        a = api.create(ContentType.Folder, workspace, None,
2369
+        a = api.create(CONTENT_TYPES.Folder.slug, workspace, None,
2370
                        'this is randomized folder', '', True)
2370
                        'this is randomized folder', '', True)
2371
-        p = api.create(ContentType.Page, workspace, a,
2371
+        p = api.create(CONTENT_TYPES.Page.slug, workspace, a,
2372
                        'this is randomized label content', '', True)
2372
                        'this is randomized label content', '', True)
2373
 
2373
 
2374
         with new_revision(
2374
         with new_revision(
2422
             session=self.session,
2422
             session=self.session,
2423
             config=self.app_config,
2423
             config=self.app_config,
2424
         )
2424
         )
2425
-        a = api.create(ContentType.Folder, workspace, None,
2425
+        a = api.create(CONTENT_TYPES.Folder.slug, workspace, None,
2426
                        'this is randomized folder', '', True)
2426
                        'this is randomized folder', '', True)
2427
-        p = api.create(ContentType.Page, workspace, a,
2427
+        p = api.create(CONTENT_TYPES.Page.slug, workspace, a,
2428
                        'this is dummy label content', '', True)
2428
                        'this is dummy label content', '', True)
2429
 
2429
 
2430
         with new_revision(
2430
         with new_revision(
2476
             config=self.app_config,
2476
             config=self.app_config,
2477
         )
2477
         )
2478
         a = api.create(
2478
         a = api.create(
2479
-            content_type=ContentType.Folder,
2479
+            content_type_slug=CONTENT_TYPES.Folder.slug,
2480
             workspace=workspace,
2480
             workspace=workspace,
2481
             parent=None,
2481
             parent=None,
2482
             label='this is randomized folder',
2482
             label='this is randomized folder',
2483
             do_save=True
2483
             do_save=True
2484
         )
2484
         )
2485
         p1 = api.create(
2485
         p1 = api.create(
2486
-            content_type=ContentType.Page,
2486
+            content_type_slug=CONTENT_TYPES.Page.slug,
2487
             workspace=workspace,
2487
             workspace=workspace,
2488
             parent=a,
2488
             parent=a,
2489
             label='this is dummy label content',
2489
             label='this is dummy label content',
2490
             do_save=True
2490
             do_save=True
2491
         )
2491
         )
2492
         p2 = api.create(
2492
         p2 = api.create(
2493
-            content_type=ContentType.Page,
2493
+            content_type_slug=CONTENT_TYPES.Page.slug,
2494
             workspace=workspace,
2494
             workspace=workspace,
2495
             parent=a,
2495
             parent=a,
2496
             label='Hey ! Jon !',
2496
             label='Hey ! Jon !',
2540
         folder_1 = self._create_content_and_test(
2540
         folder_1 = self._create_content_and_test(
2541
             'folder_1',
2541
             'folder_1',
2542
             workspace=workspace,
2542
             workspace=workspace,
2543
-            type=ContentType.Folder
2543
+            type=CONTENT_TYPES.Folder.slug
2544
         )
2544
         )
2545
         folder_2 = self._create_content_and_test(
2545
         folder_2 = self._create_content_and_test(
2546
             'folder_2',
2546
             'folder_2',
2547
             workspace=workspace,
2547
             workspace=workspace,
2548
-            type=ContentType.Folder
2548
+            type=CONTENT_TYPES.Folder.slug
2549
         )
2549
         )
2550
         page_1 = self._create_content_and_test(
2550
         page_1 = self._create_content_and_test(
2551
             'foo', workspace=workspace,
2551
             'foo', workspace=workspace,
2552
-            type=ContentType.Page,
2552
+            type=CONTENT_TYPES.Page.slug,
2553
             parent=folder_1
2553
             parent=folder_1
2554
         )
2554
         )
2555
         page_2 = self._create_content_and_test(
2555
         page_2 = self._create_content_and_test(
2556
             'bar',
2556
             'bar',
2557
             workspace=workspace,
2557
             workspace=workspace,
2558
-            type=ContentType.Page,
2558
+            type=CONTENT_TYPES.Page.slug,
2559
             parent=folder_2
2559
             parent=folder_2
2560
         )
2560
         )
2561
 
2561
 
2636
             session=self.session,
2636
             session=self.session,
2637
             config=self.app_config,
2637
             config=self.app_config,
2638
         ).create(
2638
         ).create(
2639
-            content_type=ContentType.Page,
2639
+            content_type_slug=CONTENT_TYPES.Page.slug,
2640
             workspace=bob_workspace,
2640
             workspace=bob_workspace,
2641
             label='bob_page',
2641
             label='bob_page',
2642
             do_save=True,
2642
             do_save=True,
2647
             session=self.session,
2647
             session=self.session,
2648
             config=self.app_config,
2648
             config=self.app_config,
2649
         ).create(
2649
         ).create(
2650
-            content_type=ContentType.Page,
2650
+            content_type_slug=CONTENT_TYPES.Page.slug,
2651
             workspace=admin_workspace,
2651
             workspace=admin_workspace,
2652
             label='admin_page',
2652
             label='admin_page',
2653
             do_save=True,
2653
             do_save=True,

+ 183 - 0
backend/tracim_backend/tests/library/test_user_api.py View File

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
 import pytest
2
 import pytest
3
 import transaction
3
 import transaction
4
+from tracim_backend import models
4
 
5
 
5
 from tracim_backend.exceptions import AuthenticationFailed
6
 from tracim_backend.exceptions import AuthenticationFailed
7
+from tracim_backend.exceptions import TooShortAutocompleteString
6
 from tracim_backend.exceptions import UserDoesNotExist
8
 from tracim_backend.exceptions import UserDoesNotExist
7
 from tracim_backend.exceptions import UserNotActive
9
 from tracim_backend.exceptions import UserNotActive
8
 from tracim_backend.lib.core.group import GroupApi
10
 from tracim_backend.lib.core.group import GroupApi
9
 from tracim_backend.lib.core.user import UserApi
11
 from tracim_backend.lib.core.user import UserApi
12
+from tracim_backend.lib.core.userworkspace import RoleApi
13
+from tracim_backend.lib.core.workspace import WorkspaceApi
10
 from tracim_backend.models import User
14
 from tracim_backend.models import User
11
 from tracim_backend.models.context_models import UserInContext
15
 from tracim_backend.models.context_models import UserInContext
16
+from tracim_backend.models.data import UserRoleInWorkspace
12
 from tracim_backend.tests import DefaultTest
17
 from tracim_backend.tests import DefaultTest
13
 from tracim_backend.tests import eq_
18
 from tracim_backend.tests import eq_
14
 
19
 
107
         # u1 + Admin user from BaseFixture
112
         # u1 + Admin user from BaseFixture
108
         assert 2 == len(users)
113
         assert 2 == len(users)
109
 
114
 
115
+    def test_unit__get_known__user__admin__too_short_acp_str(self):
116
+        api = UserApi(
117
+            current_user=None,
118
+            session=self.session,
119
+            config=self.config,
120
+        )
121
+        u1 = api.create_user(
122
+            email='email@email',
123
+            name='name',
124
+            do_notify=False,
125
+            do_save=True,
126
+        )
127
+        with pytest.raises(TooShortAutocompleteString):
128
+            api.get_known_user('e')
129
+
130
+    def test_unit__get_known__user__admin__by_email(self):
131
+        api = UserApi(
132
+            current_user=None,
133
+            session=self.session,
134
+            config=self.config,
135
+        )
136
+        u1 = api.create_user(
137
+            email='email@email',
138
+            name='name',
139
+            do_notify=False,
140
+            do_save=True,
141
+        )
142
+
143
+        users = api.get_known_user('email')
144
+        assert len(users) == 1
145
+        assert users[0] == u1
146
+
147
+    def test_unit__get_known__user__user__no_workspace_empty_known_user(self):
148
+        admin = self.session.query(models.User) \
149
+            .filter(models.User.email == 'admin@admin.admin') \
150
+            .one()
151
+        api = UserApi(
152
+            current_user=admin,
153
+            session=self.session,
154
+            config=self.config,
155
+        )
156
+        u1 = api.create_user(
157
+            email='email@email',
158
+            name='name',
159
+            do_notify=False,
160
+            do_save=True,
161
+        )
162
+        api2 = UserApi(
163
+            current_user=u1,
164
+            session=self.session,
165
+            config=self.config,
166
+        )
167
+        users = api2.get_known_user('email')
168
+        assert len(users) == 0
169
+
170
+    def test_unit__get_known__user__same_workspaces_users_by_name(self):
171
+        admin = self.session.query(models.User) \
172
+            .filter(models.User.email == 'admin@admin.admin') \
173
+            .one()
174
+        api = UserApi(
175
+            current_user=None,
176
+            session=self.session,
177
+            config=self.config,
178
+        )
179
+        u1 = api.create_user(
180
+            email='email@email',
181
+            name='name',
182
+            do_notify=False,
183
+            do_save=True,
184
+        )
185
+        u2 = api.create_user(
186
+            email='email2@email2',
187
+            name='name2',
188
+            do_notify=False,
189
+            do_save=True,
190
+        )
191
+        u3 = api.create_user(
192
+            email='notfound@notfound',
193
+            name='notfound',
194
+            do_notify=False,
195
+            do_save=True,
196
+        )
197
+        wapi = WorkspaceApi(
198
+            current_user=admin,
199
+            session=self.session,
200
+            config=self.app_config,
201
+        )
202
+        workspace = wapi.create_workspace(
203
+            'test workspace n°1',
204
+            save_now=True)
205
+        role_api = RoleApi(
206
+            current_user=admin,
207
+            session=self.session,
208
+            config=self.app_config,
209
+        )
210
+        role_api.create_one(u1, workspace, UserRoleInWorkspace.READER, False)
211
+        role_api.create_one(u2, workspace, UserRoleInWorkspace.READER, False)
212
+        role_api.create_one(u3, workspace, UserRoleInWorkspace.READER, False)
213
+        api2 = UserApi(
214
+            current_user=u1,
215
+            session=self.session,
216
+            config=self.config,
217
+        )
218
+        users = api2.get_known_user('name')
219
+        assert len(users) == 2
220
+        assert users[0] == u1
221
+        assert users[1] == u2
222
+
223
+    def test_unit__get_known__user__same_workspaces_users_by_email(self):
224
+        admin = self.session.query(models.User) \
225
+            .filter(models.User.email == 'admin@admin.admin') \
226
+            .one()
227
+        api = UserApi(
228
+            current_user=None,
229
+            session=self.session,
230
+            config=self.config,
231
+        )
232
+        u1 = api.create_user(
233
+            email='email@email',
234
+            name='name',
235
+            do_notify=False,
236
+            do_save=True,
237
+        )
238
+        u2 = api.create_user(
239
+            email='email2@email2',
240
+            name='name2',
241
+            do_notify=False,
242
+            do_save=True,
243
+        )
244
+        u3 = api.create_user(
245
+            email='notfound@notfound',
246
+            name='notfound',
247
+            do_notify=False,
248
+            do_save=True,
249
+        )
250
+        wapi = WorkspaceApi(
251
+            current_user=admin,
252
+            session=self.session,
253
+            config=self.app_config,
254
+        )
255
+        workspace = wapi.create_workspace(
256
+            'test workspace n°1',
257
+            save_now=True)
258
+        role_api = RoleApi(
259
+            current_user=admin,
260
+            session=self.session,
261
+            config=self.app_config,
262
+        )
263
+        role_api.create_one(u1, workspace, UserRoleInWorkspace.READER, False)
264
+        role_api.create_one(u2, workspace, UserRoleInWorkspace.READER, False)
265
+        role_api.create_one(u3, workspace, UserRoleInWorkspace.READER, False)
266
+        api2 = UserApi(
267
+            current_user=u1,
268
+            session=self.session,
269
+            config=self.config,
270
+        )
271
+        users = api2.get_known_user('email')
272
+        assert len(users) == 2
273
+        assert users[0] == u1
274
+        assert users[1] == u2
275
+
276
+    def test_unit__get_known__user__admin__by_name(self):
277
+        api = UserApi(
278
+            current_user=None,
279
+            session=self.session,
280
+            config=self.config,
281
+        )
282
+        u1 = api.create_user(
283
+            email='email@email',
284
+            name='name',
285
+            do_notify=False,
286
+            do_save=True,
287
+        )
288
+
289
+        users = api.get_known_user('nam')
290
+        assert len(users) == 1
291
+        assert users[0] == u1
292
+
110
     def test_unit__get_one__ok__nominal_case(self):
293
     def test_unit__get_one__ok__nominal_case(self):
111
         api = UserApi(
294
         api = UserApi(
112
             current_user=None,
295
             current_user=None,

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

29
         :return:
29
         :return:
30
         """
30
         """
31
         tracim_settings = {
31
         tracim_settings = {
32
+            'website.base_url': 'http://localhost:6543',
32
             'sqlalchemy.url': 'sqlite:///:memory:',
33
             'sqlalchemy.url': 'sqlite:///:memory:',
33
             'user.auth_token.validity': '604800',
34
             'user.auth_token.validity': '604800',
34
             'depot_storage_dir': '/tmp/test/depot',
35
             'depot_storage_dir': '/tmp/test/depot',

+ 14 - 0
backend/tracim_backend/tests/library/tests_utils.py View File

1
+import string
2
+
3
+from tracim_backend.lib.utils.utils import password_generator
4
+from tracim_backend.lib.utils.utils import ALLOWED_AUTOGEN_PASSWORD_CHAR
5
+from tracim_backend.lib.utils.utils import DEFAULT_PASSWORD_GEN_CHAR_LENGTH
6
+
7
+
8
+class TestPasswordGenerator(object):
9
+
10
+    def test_password_generator_ok_nominal_case(self):
11
+        password = password_generator()
12
+        assert len(password) == DEFAULT_PASSWORD_GEN_CHAR_LENGTH
13
+        for char in password:
14
+            assert char in ALLOWED_AUTOGEN_PASSWORD_CHAR

+ 6 - 6
backend/tracim_backend/tests/models/test_content.py View File

15
 from tracim_backend.models import User
15
 from tracim_backend.models import User
16
 from tracim_backend.models.data import ActionDescription
16
 from tracim_backend.models.data import ActionDescription
17
 from tracim_backend.models.data import ContentRevisionRO
17
 from tracim_backend.models.data import ContentRevisionRO
18
-from tracim_backend.models.data import ContentType
18
+from tracim_backend.models.contents import CONTENT_TYPES
19
 from tracim_backend.models.data import Workspace
19
 from tracim_backend.models.data import Workspace
20
 from tracim_backend.tests import StandardTest
20
 from tracim_backend.tests import StandardTest
21
 
21
 
95
 
95
 
96
         content1_from_api = api.get_one(
96
         content1_from_api = api.get_one(
97
             content1.id,
97
             content1.id,
98
-            ContentType.Page,
98
+            CONTENT_TYPES.Page.slug,
99
             workspace1
99
             workspace1
100
         )
100
         )
101
 
101
 
196
         first_content = self._create_content(
196
         first_content = self._create_content(
197
             owner=user_admin,
197
             owner=user_admin,
198
             workspace=workspace,
198
             workspace=workspace,
199
-            type=ContentType.Page,
199
+            type=CONTENT_TYPES.Page.slug,
200
             label='TEST_CONTENT_1',
200
             label='TEST_CONTENT_1',
201
             description='TEST_CONTENT_DESCRIPTION_1',
201
             description='TEST_CONTENT_DESCRIPTION_1',
202
             revision_type=ActionDescription.CREATION,
202
             revision_type=ActionDescription.CREATION,
221
         second_content = self._create_content(
221
         second_content = self._create_content(
222
             owner=user_admin,
222
             owner=user_admin,
223
             workspace=workspace,
223
             workspace=workspace,
224
-            type=ContentType.Page,
224
+            type=CONTENT_TYPES.Page.slug,
225
             label='TEST_CONTENT_2',
225
             label='TEST_CONTENT_2',
226
             description='TEST_CONTENT_DESCRIPTION_2',
226
             description='TEST_CONTENT_DESCRIPTION_2',
227
             revision_type=ActionDescription.CREATION
227
             revision_type=ActionDescription.CREATION
268
         created_content = self._create_content(
268
         created_content = self._create_content(
269
             owner=user_admin,
269
             owner=user_admin,
270
             workspace=workspace,
270
             workspace=workspace,
271
-            type=ContentType.Page,
271
+            type=CONTENT_TYPES.Page.slug,
272
             label='TEST_CONTENT_%s' % key,
272
             label='TEST_CONTENT_%s' % key,
273
             description='TEST_CONTENT_DESCRIPTION_%s' % key,
273
             description='TEST_CONTENT_DESCRIPTION_%s' % key,
274
             revision_type=ActionDescription.CREATION
274
             revision_type=ActionDescription.CREATION
308
         content = self._create_content(
308
         content = self._create_content(
309
             owner=user_admin,
309
             owner=user_admin,
310
             workspace=workspace,
310
             workspace=workspace,
311
-            type=ContentType.File,
311
+            type=CONTENT_TYPES.File.slug,
312
             label='TEST_CONTENT_1',
312
             label='TEST_CONTENT_1',
313
             description='TEST_CONTENT_DESCRIPTION_1',
313
             description='TEST_CONTENT_DESCRIPTION_1',
314
             revision_type=ActionDescription.CREATION,
314
             revision_type=ActionDescription.CREATION,

+ 4 - 3
backend/tracim_backend/tests/models/test_content_revision.py View File

5
 
5
 
6
 from tracim_backend.models import ContentRevisionRO
6
 from tracim_backend.models import ContentRevisionRO
7
 from tracim_backend.models import User
7
 from tracim_backend.models import User
8
-from tracim_backend.models.data import ContentType
8
+from tracim_backend.models.contents import CONTENT_TYPES
9
 from tracim_backend.tests import DefaultTest
9
 from tracim_backend.tests import DefaultTest
10
 from tracim_backend.tests import eq_
10
 from tracim_backend.tests import eq_
11
 
11
 
12
+
12
 class TestContentRevision(DefaultTest):
13
 class TestContentRevision(DefaultTest):
13
 
14
 
14
     def _new_from(self, revision):
15
     def _new_from(self, revision):
47
         folder = self._create_content_and_test(
48
         folder = self._create_content_and_test(
48
             name='folder_1',
49
             name='folder_1',
49
             workspace=workspace,
50
             workspace=workspace,
50
-            type=ContentType.Folder
51
+            type=CONTENT_TYPES.Folder.slug
51
         )
52
         )
52
         page = self._create_content_and_test(
53
         page = self._create_content_and_test(
53
             workspace=workspace,
54
             workspace=workspace,
54
             parent=folder,
55
             parent=folder,
55
             name='file_1',
56
             name='file_1',
56
             description='content of file_1',
57
             description='content of file_1',
57
-            type=ContentType.Page,
58
+            type=CONTENT_TYPES.Page.slug,
58
             owner=admin
59
             owner=admin
59
         )
60
         )
60
 
61
 

+ 11 - 5
backend/tracim_backend/views/contents_api/comment_controller.py View File

20
 from tracim_backend.views.core_api.schemas import WorkspaceAndContentIdPathSchema
20
 from tracim_backend.views.core_api.schemas import WorkspaceAndContentIdPathSchema
21
 from tracim_backend.views.core_api.schemas import NoContentSchema
21
 from tracim_backend.views.core_api.schemas import NoContentSchema
22
 from tracim_backend.exceptions import EmptyCommentContentNotAllowed
22
 from tracim_backend.exceptions import EmptyCommentContentNotAllowed
23
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
23
+from tracim_backend.models.contents import CONTENT_TYPES
24
 from tracim_backend.models.revision_protection import new_revision
24
 from tracim_backend.models.revision_protection import new_revision
25
 from tracim_backend.models.data import UserRoleInWorkspace
25
 from tracim_backend.models.data import UserRoleInWorkspace
26
 
26
 
41
         # login = hapic_data.body
41
         # login = hapic_data.body
42
         app_config = request.registry.settings['CFG']
42
         app_config = request.registry.settings['CFG']
43
         api = ContentApi(
43
         api = ContentApi(
44
+            show_archived=True,
45
+            show_deleted=True,
44
             current_user=request.current_user,
46
             current_user=request.current_user,
45
             session=request.dbsession,
47
             session=request.dbsession,
46
             config=app_config,
48
             config=app_config,
47
         )
49
         )
48
         content = api.get_one(
50
         content = api.get_one(
49
             hapic_data.path.content_id,
51
             hapic_data.path.content_id,
50
-            content_type=ContentType.Any
52
+            content_type=CONTENT_TYPES.Any_SLUG
51
         )
53
         )
52
         comments = content.get_comments()
54
         comments = content.get_comments()
53
         comments.sort(key=lambda comment: comment.created)
55
         comments.sort(key=lambda comment: comment.created)
68
         # login = hapic_data.body
70
         # login = hapic_data.body
69
         app_config = request.registry.settings['CFG']
71
         app_config = request.registry.settings['CFG']
70
         api = ContentApi(
72
         api = ContentApi(
73
+            show_archived=True,
74
+            show_deleted=True,
71
             current_user=request.current_user,
75
             current_user=request.current_user,
72
             session=request.dbsession,
76
             session=request.dbsession,
73
             config=app_config,
77
             config=app_config,
74
         )
78
         )
75
         content = api.get_one(
79
         content = api.get_one(
76
             hapic_data.path.content_id,
80
             hapic_data.path.content_id,
77
-            content_type=ContentType.Any
81
+            content_type=CONTENT_TYPES.Any_SLUG
78
         )
82
         )
79
         comment = api.create_comment(
83
         comment = api.create_comment(
80
             content.workspace,
84
             content.workspace,
97
         """
101
         """
98
         app_config = request.registry.settings['CFG']
102
         app_config = request.registry.settings['CFG']
99
         api = ContentApi(
103
         api = ContentApi(
104
+            show_archived=True,
105
+            show_deleted=True,
100
             current_user=request.current_user,
106
             current_user=request.current_user,
101
             session=request.dbsession,
107
             session=request.dbsession,
102
             config=app_config,
108
             config=app_config,
109
         workspace = wapi.get_one(hapic_data.path.workspace_id)
115
         workspace = wapi.get_one(hapic_data.path.workspace_id)
110
         parent = api.get_one(
116
         parent = api.get_one(
111
             hapic_data.path.content_id,
117
             hapic_data.path.content_id,
112
-            content_type=ContentType.Any,
118
+            content_type=CONTENT_TYPES.Any_SLUG,
113
             workspace=workspace
119
             workspace=workspace
114
         )
120
         )
115
         comment = api.get_one(
121
         comment = api.get_one(
116
             hapic_data.path.comment_id,
122
             hapic_data.path.comment_id,
117
-            content_type=ContentType.Comment,
123
+            content_type=CONTENT_TYPES.Comment.slug,
118
             workspace=workspace,
124
             workspace=workspace,
119
             parent=parent,
125
             parent=parent,
120
         )
126
         )

+ 42 - 14
backend/tracim_backend/views/contents_api/file_controller.py View File

32
 from tracim_backend.models.data import UserRoleInWorkspace
32
 from tracim_backend.models.data import UserRoleInWorkspace
33
 from tracim_backend.models.context_models import ContentInContext
33
 from tracim_backend.models.context_models import ContentInContext
34
 from tracim_backend.models.context_models import RevisionInContext
34
 from tracim_backend.models.context_models import RevisionInContext
35
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
35
+from tracim_backend.models.contents import CONTENT_TYPES
36
 from tracim_backend.models.contents import file_type
36
 from tracim_backend.models.contents import file_type
37
 from tracim_backend.models.revision_protection import new_revision
37
 from tracim_backend.models.revision_protection import new_revision
38
 from tracim_backend.exceptions import EmptyLabelNotAllowed
38
 from tracim_backend.exceptions import EmptyLabelNotAllowed
61
         """
61
         """
62
         app_config = request.registry.settings['CFG']
62
         app_config = request.registry.settings['CFG']
63
         api = ContentApi(
63
         api = ContentApi(
64
+            show_archived=True,
65
+            show_deleted=True,
64
             current_user=request.current_user,
66
             current_user=request.current_user,
65
             session=request.dbsession,
67
             session=request.dbsession,
66
             config=app_config,
68
             config=app_config,
67
         )
69
         )
68
         content = api.get_one(
70
         content = api.get_one(
69
             hapic_data.path.content_id,
71
             hapic_data.path.content_id,
70
-            content_type=ContentType.Any
72
+            content_type=CONTENT_TYPES.Any_SLUG
71
         )
73
         )
72
         file = request.POST['files']
74
         file = request.POST['files']
73
         with new_revision(
75
         with new_revision(
95
         """
97
         """
96
         app_config = request.registry.settings['CFG']
98
         app_config = request.registry.settings['CFG']
97
         api = ContentApi(
99
         api = ContentApi(
100
+            show_archived=True,
101
+            show_deleted=True,
98
             current_user=request.current_user,
102
             current_user=request.current_user,
99
             session=request.dbsession,
103
             session=request.dbsession,
100
             config=app_config,
104
             config=app_config,
101
         )
105
         )
102
         content = api.get_one(
106
         content = api.get_one(
103
             hapic_data.path.content_id,
107
             hapic_data.path.content_id,
104
-            content_type=ContentType.Any
108
+            content_type=CONTENT_TYPES.Any_SLUG
105
         )
109
         )
106
         file = DepotManager.get().get(content.depot_file)
110
         file = DepotManager.get().get(content.depot_file)
107
         response = request.response
111
         response = request.response
120
         """
124
         """
121
         app_config = request.registry.settings['CFG']
125
         app_config = request.registry.settings['CFG']
122
         api = ContentApi(
126
         api = ContentApi(
127
+            show_archived=True,
128
+            show_deleted=True,
123
             current_user=request.current_user,
129
             current_user=request.current_user,
124
             session=request.dbsession,
130
             session=request.dbsession,
125
             config=app_config,
131
             config=app_config,
126
         )
132
         )
127
         content = api.get_one(
133
         content = api.get_one(
128
             hapic_data.path.content_id,
134
             hapic_data.path.content_id,
129
-            content_type=ContentType.Any
135
+            content_type=CONTENT_TYPES.Any_SLUG
130
         )
136
         )
131
         revision = api.get_one_revision(
137
         revision = api.get_one_revision(
132
             revision_id=hapic_data.path.revision_id,
138
             revision_id=hapic_data.path.revision_id,
154
         """
160
         """
155
         app_config = request.registry.settings['CFG']
161
         app_config = request.registry.settings['CFG']
156
         api = ContentApi(
162
         api = ContentApi(
163
+            show_archived=True,
164
+            show_deleted=True,
157
             current_user=request.current_user,
165
             current_user=request.current_user,
158
             session=request.dbsession,
166
             session=request.dbsession,
159
             config=app_config,
167
             config=app_config,
160
         )
168
         )
161
         content = api.get_one(
169
         content = api.get_one(
162
             hapic_data.path.content_id,
170
             hapic_data.path.content_id,
163
-            content_type=ContentType.Any
171
+            content_type=CONTENT_TYPES.Any_SLUG
164
         )
172
         )
165
         pdf_preview_path = api.get_pdf_preview_path(
173
         pdf_preview_path = api.get_pdf_preview_path(
166
             content.content_id,
174
             content.content_id,
181
         """
189
         """
182
         app_config = request.registry.settings['CFG']
190
         app_config = request.registry.settings['CFG']
183
         api = ContentApi(
191
         api = ContentApi(
192
+            show_archived=True,
193
+            show_deleted=True,
184
             current_user=request.current_user,
194
             current_user=request.current_user,
185
             session=request.dbsession,
195
             session=request.dbsession,
186
             config=app_config,
196
             config=app_config,
187
         )
197
         )
188
         content = api.get_one(
198
         content = api.get_one(
189
             hapic_data.path.content_id,
199
             hapic_data.path.content_id,
190
-            content_type=ContentType.Any
200
+            content_type=CONTENT_TYPES.Any_SLUG
191
         )
201
         )
192
         pdf_preview_path = api.get_full_pdf_preview_path(content.revision_id)
202
         pdf_preview_path = api.get_full_pdf_preview_path(content.revision_id)
193
         return FileResponse(pdf_preview_path)
203
         return FileResponse(pdf_preview_path)
205
         """
215
         """
206
         app_config = request.registry.settings['CFG']
216
         app_config = request.registry.settings['CFG']
207
         api = ContentApi(
217
         api = ContentApi(
218
+            show_archived=True,
219
+            show_deleted=True,
208
             current_user=request.current_user,
220
             current_user=request.current_user,
209
             session=request.dbsession,
221
             session=request.dbsession,
210
             config=app_config,
222
             config=app_config,
211
         )
223
         )
212
         content = api.get_one(
224
         content = api.get_one(
213
             hapic_data.path.content_id,
225
             hapic_data.path.content_id,
214
-            content_type=ContentType.Any
226
+            content_type=CONTENT_TYPES.Any_SLUG
215
         )
227
         )
216
         revision = api.get_one_revision(
228
         revision = api.get_one_revision(
217
             revision_id=hapic_data.path.revision_id,
229
             revision_id=hapic_data.path.revision_id,
238
         """
250
         """
239
         app_config = request.registry.settings['CFG']
251
         app_config = request.registry.settings['CFG']
240
         api = ContentApi(
252
         api = ContentApi(
253
+            show_archived=True,
254
+            show_deleted=True,
241
             current_user=request.current_user,
255
             current_user=request.current_user,
242
             session=request.dbsession,
256
             session=request.dbsession,
243
             config=app_config,
257
             config=app_config,
244
         )
258
         )
245
         content = api.get_one(
259
         content = api.get_one(
246
             hapic_data.path.content_id,
260
             hapic_data.path.content_id,
247
-            content_type=ContentType.Any
261
+            content_type=CONTENT_TYPES.Any_SLUG
248
         )
262
         )
249
         allowed_dim = api.get_jpg_preview_allowed_dim()
263
         allowed_dim = api.get_jpg_preview_allowed_dim()
250
         jpg_preview_path = api.get_jpg_preview_path(
264
         jpg_preview_path = api.get_jpg_preview_path(
270
         """
284
         """
271
         app_config = request.registry.settings['CFG']
285
         app_config = request.registry.settings['CFG']
272
         api = ContentApi(
286
         api = ContentApi(
287
+            show_archived=True,
288
+            show_deleted=True,
273
             current_user=request.current_user,
289
             current_user=request.current_user,
274
             session=request.dbsession,
290
             session=request.dbsession,
275
             config=app_config,
291
             config=app_config,
276
         )
292
         )
277
         content = api.get_one(
293
         content = api.get_one(
278
             hapic_data.path.content_id,
294
             hapic_data.path.content_id,
279
-            content_type=ContentType.Any
295
+            content_type=CONTENT_TYPES.Any_SLUG
280
         )
296
         )
281
         jpg_preview_path = api.get_jpg_preview_path(
297
         jpg_preview_path = api.get_jpg_preview_path(
282
             content_id=content.content_id,
298
             content_id=content.content_id,
301
         """
317
         """
302
         app_config = request.registry.settings['CFG']
318
         app_config = request.registry.settings['CFG']
303
         api = ContentApi(
319
         api = ContentApi(
320
+            show_archived=True,
321
+            show_deleted=True,
304
             current_user=request.current_user,
322
             current_user=request.current_user,
305
             session=request.dbsession,
323
             session=request.dbsession,
306
             config=app_config,
324
             config=app_config,
307
         )
325
         )
308
         content = api.get_one(
326
         content = api.get_one(
309
             hapic_data.path.content_id,
327
             hapic_data.path.content_id,
310
-            content_type=ContentType.Any
328
+            content_type=CONTENT_TYPES.Any_SLUG
311
         )
329
         )
312
         revision = api.get_one_revision(
330
         revision = api.get_one_revision(
313
             revision_id=hapic_data.path.revision_id,
331
             revision_id=hapic_data.path.revision_id,
334
         """
352
         """
335
         app_config = request.registry.settings['CFG']
353
         app_config = request.registry.settings['CFG']
336
         api = ContentApi(
354
         api = ContentApi(
355
+            show_archived=True,
356
+            show_deleted=True,
337
             current_user=request.current_user,
357
             current_user=request.current_user,
338
             session=request.dbsession,
358
             session=request.dbsession,
339
             config=app_config,
359
             config=app_config,
352
         """
372
         """
353
         app_config = request.registry.settings['CFG']
373
         app_config = request.registry.settings['CFG']
354
         api = ContentApi(
374
         api = ContentApi(
375
+            show_archived=True,
376
+            show_deleted=True,
355
             current_user=request.current_user,
377
             current_user=request.current_user,
356
             session=request.dbsession,
378
             session=request.dbsession,
357
             config=app_config,
379
             config=app_config,
358
         )
380
         )
359
         content = api.get_one(
381
         content = api.get_one(
360
             hapic_data.path.content_id,
382
             hapic_data.path.content_id,
361
-            content_type=ContentType.Any
383
+            content_type=CONTENT_TYPES.Any_SLUG
362
         )
384
         )
363
         return api.get_content_in_context(content)
385
         return api.get_content_in_context(content)
364
 
386
 
375
         """
397
         """
376
         app_config = request.registry.settings['CFG']
398
         app_config = request.registry.settings['CFG']
377
         api = ContentApi(
399
         api = ContentApi(
400
+            show_archived=True,
401
+            show_deleted=True,
378
             current_user=request.current_user,
402
             current_user=request.current_user,
379
             session=request.dbsession,
403
             session=request.dbsession,
380
             config=app_config,
404
             config=app_config,
381
         )
405
         )
382
         content = api.get_one(
406
         content = api.get_one(
383
             hapic_data.path.content_id,
407
             hapic_data.path.content_id,
384
-            content_type=ContentType.Any
408
+            content_type=CONTENT_TYPES.Any_SLUG
385
         )
409
         )
386
         with new_revision(
410
         with new_revision(
387
                 session=request.dbsession,
411
                 session=request.dbsession,
413
         """
437
         """
414
         app_config = request.registry.settings['CFG']
438
         app_config = request.registry.settings['CFG']
415
         api = ContentApi(
439
         api = ContentApi(
440
+            show_archived=True,
441
+            show_deleted=True,
416
             current_user=request.current_user,
442
             current_user=request.current_user,
417
             session=request.dbsession,
443
             session=request.dbsession,
418
             config=app_config,
444
             config=app_config,
419
         )
445
         )
420
         content = api.get_one(
446
         content = api.get_one(
421
             hapic_data.path.content_id,
447
             hapic_data.path.content_id,
422
-            content_type=ContentType.Any
448
+            content_type=CONTENT_TYPES.Any_SLUG
423
         )
449
         )
424
         revisions = content.revisions
450
         revisions = content.revisions
425
         return [
451
         return [
440
         """
466
         """
441
         app_config = request.registry.settings['CFG']
467
         app_config = request.registry.settings['CFG']
442
         api = ContentApi(
468
         api = ContentApi(
469
+            show_archived=True,
470
+            show_deleted=True,
443
             current_user=request.current_user,
471
             current_user=request.current_user,
444
             session=request.dbsession,
472
             session=request.dbsession,
445
             config=app_config,
473
             config=app_config,
446
         )
474
         )
447
         content = api.get_one(
475
         content = api.get_one(
448
             hapic_data.path.content_id,
476
             hapic_data.path.content_id,
449
-            content_type=ContentType.Any
477
+            content_type=CONTENT_TYPES.Any_SLUG
450
         )
478
         )
451
         with new_revision(
479
         with new_revision(
452
                 session=request.dbsession,
480
                 session=request.dbsession,

+ 13 - 5
backend/tracim_backend/views/contents_api/html_document_controller.py View File

26
 from tracim_backend.exceptions import EmptyLabelNotAllowed
26
 from tracim_backend.exceptions import EmptyLabelNotAllowed
27
 from tracim_backend.models.context_models import ContentInContext
27
 from tracim_backend.models.context_models import ContentInContext
28
 from tracim_backend.models.context_models import RevisionInContext
28
 from tracim_backend.models.context_models import RevisionInContext
29
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
29
+from tracim_backend.models.contents import CONTENT_TYPES
30
 from tracim_backend.models.contents import html_documents_type
30
 from tracim_backend.models.contents import html_documents_type
31
 from tracim_backend.models.revision_protection import new_revision
31
 from tracim_backend.models.revision_protection import new_revision
32
 
32
 
46
         """
46
         """
47
         app_config = request.registry.settings['CFG']
47
         app_config = request.registry.settings['CFG']
48
         api = ContentApi(
48
         api = ContentApi(
49
+            show_archived=True,
50
+            show_deleted=True,
49
             current_user=request.current_user,
51
             current_user=request.current_user,
50
             session=request.dbsession,
52
             session=request.dbsession,
51
             config=app_config,
53
             config=app_config,
52
         )
54
         )
53
         content = api.get_one(
55
         content = api.get_one(
54
             hapic_data.path.content_id,
56
             hapic_data.path.content_id,
55
-            content_type=ContentType.Any
57
+            content_type=CONTENT_TYPES.Any_SLUG
56
         )
58
         )
57
         return api.get_content_in_context(content)
59
         return api.get_content_in_context(content)
58
 
60
 
69
         """
71
         """
70
         app_config = request.registry.settings['CFG']
72
         app_config = request.registry.settings['CFG']
71
         api = ContentApi(
73
         api = ContentApi(
74
+            show_archived=True,
75
+            show_deleted=True,
72
             current_user=request.current_user,
76
             current_user=request.current_user,
73
             session=request.dbsession,
77
             session=request.dbsession,
74
             config=app_config,
78
             config=app_config,
75
         )
79
         )
76
         content = api.get_one(
80
         content = api.get_one(
77
             hapic_data.path.content_id,
81
             hapic_data.path.content_id,
78
-            content_type=ContentType.Any
82
+            content_type=CONTENT_TYPES.Any_SLUG
79
         )
83
         )
80
         with new_revision(
84
         with new_revision(
81
                 session=request.dbsession,
85
                 session=request.dbsession,
107
         """
111
         """
108
         app_config = request.registry.settings['CFG']
112
         app_config = request.registry.settings['CFG']
109
         api = ContentApi(
113
         api = ContentApi(
114
+            show_archived=True,
115
+            show_deleted=True,
110
             current_user=request.current_user,
116
             current_user=request.current_user,
111
             session=request.dbsession,
117
             session=request.dbsession,
112
             config=app_config,
118
             config=app_config,
113
         )
119
         )
114
         content = api.get_one(
120
         content = api.get_one(
115
             hapic_data.path.content_id,
121
             hapic_data.path.content_id,
116
-            content_type=ContentType.Any
122
+            content_type=CONTENT_TYPES.Any_SLUG
117
         )
123
         )
118
         revisions = content.revisions
124
         revisions = content.revisions
119
         return [
125
         return [
138
         """
144
         """
139
         app_config = request.registry.settings['CFG']
145
         app_config = request.registry.settings['CFG']
140
         api = ContentApi(
146
         api = ContentApi(
147
+            show_archived=True,
148
+            show_deleted=True,
141
             current_user=request.current_user,
149
             current_user=request.current_user,
142
             session=request.dbsession,
150
             session=request.dbsession,
143
             config=app_config,
151
             config=app_config,
144
         )
152
         )
145
         content = api.get_one(
153
         content = api.get_one(
146
             hapic_data.path.content_id,
154
             hapic_data.path.content_id,
147
-            content_type=ContentType.Any
155
+            content_type=CONTENT_TYPES.Any_SLUG
148
         )
156
         )
149
         with new_revision(
157
         with new_revision(
150
                 session=request.dbsession,
158
                 session=request.dbsession,

+ 13 - 5
backend/tracim_backend/views/contents_api/threads_controller.py View File

25
 from tracim_backend.exceptions import EmptyLabelNotAllowed
25
 from tracim_backend.exceptions import EmptyLabelNotAllowed
26
 from tracim_backend.models.context_models import ContentInContext
26
 from tracim_backend.models.context_models import ContentInContext
27
 from tracim_backend.models.context_models import RevisionInContext
27
 from tracim_backend.models.context_models import RevisionInContext
28
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
28
+from tracim_backend.models.contents import CONTENT_TYPES
29
 from tracim_backend.models.contents import thread_type
29
 from tracim_backend.models.contents import thread_type
30
 from tracim_backend.models.revision_protection import new_revision
30
 from tracim_backend.models.revision_protection import new_revision
31
 
31
 
45
         """
45
         """
46
         app_config = request.registry.settings['CFG']
46
         app_config = request.registry.settings['CFG']
47
         api = ContentApi(
47
         api = ContentApi(
48
+            show_archived=True,
49
+            show_deleted=True,
48
             current_user=request.current_user,
50
             current_user=request.current_user,
49
             session=request.dbsession,
51
             session=request.dbsession,
50
             config=app_config,
52
             config=app_config,
51
         )
53
         )
52
         content = api.get_one(
54
         content = api.get_one(
53
             hapic_data.path.content_id,
55
             hapic_data.path.content_id,
54
-            content_type=ContentType.Any
56
+            content_type=CONTENT_TYPES.Any_SLUG
55
         )
57
         )
56
         return api.get_content_in_context(content)
58
         return api.get_content_in_context(content)
57
 
59
 
68
         """
70
         """
69
         app_config = request.registry.settings['CFG']
71
         app_config = request.registry.settings['CFG']
70
         api = ContentApi(
72
         api = ContentApi(
73
+            show_archived=True,
74
+            show_deleted=True,
71
             current_user=request.current_user,
75
             current_user=request.current_user,
72
             session=request.dbsession,
76
             session=request.dbsession,
73
             config=app_config,
77
             config=app_config,
74
         )
78
         )
75
         content = api.get_one(
79
         content = api.get_one(
76
             hapic_data.path.content_id,
80
             hapic_data.path.content_id,
77
-            content_type=ContentType.Any
81
+            content_type=CONTENT_TYPES.Any_SLUG
78
         )
82
         )
79
         with new_revision(
83
         with new_revision(
80
                 session=request.dbsession,
84
                 session=request.dbsession,
106
         """
110
         """
107
         app_config = request.registry.settings['CFG']
111
         app_config = request.registry.settings['CFG']
108
         api = ContentApi(
112
         api = ContentApi(
113
+            show_archived=True,
114
+            show_deleted=True,
109
             current_user=request.current_user,
115
             current_user=request.current_user,
110
             session=request.dbsession,
116
             session=request.dbsession,
111
             config=app_config,
117
             config=app_config,
112
         )
118
         )
113
         content = api.get_one(
119
         content = api.get_one(
114
             hapic_data.path.content_id,
120
             hapic_data.path.content_id,
115
-            content_type=ContentType.Any
121
+            content_type=CONTENT_TYPES.Any_SLUG
116
         )
122
         )
117
         revisions = content.revisions
123
         revisions = content.revisions
118
         return [
124
         return [
132
         """
138
         """
133
         app_config = request.registry.settings['CFG']
139
         app_config = request.registry.settings['CFG']
134
         api = ContentApi(
140
         api = ContentApi(
141
+            show_archived=True,
142
+            show_deleted=True,
135
             current_user=request.current_user,
143
             current_user=request.current_user,
136
             session=request.dbsession,
144
             session=request.dbsession,
137
             config=app_config,
145
             config=app_config,
138
         )
146
         )
139
         content = api.get_one(
147
         content = api.get_one(
140
             hapic_data.path.content_id,
148
             hapic_data.path.content_id,
141
-            content_type=ContentType.Any
149
+            content_type=CONTENT_TYPES.Any_SLUG
142
         )
150
         )
143
         with new_revision(
151
         with new_revision(
144
                 session=request.dbsession,
152
                 session=request.dbsession,

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

2
 import marshmallow
2
 import marshmallow
3
 from marshmallow import post_load
3
 from marshmallow import post_load
4
 from marshmallow.validate import OneOf
4
 from marshmallow.validate import OneOf
5
+from marshmallow.validate import Length
5
 from marshmallow.validate import Range
6
 from marshmallow.validate import Range
6
 
7
 
7
 from tracim_backend.lib.utils.utils import DATETIME_FORMAT
8
 from tracim_backend.lib.utils.utils import DATETIME_FORMAT
8
 from tracim_backend.models.auth import Profile
9
 from tracim_backend.models.auth import Profile
9
 from tracim_backend.models.contents import GlobalStatus
10
 from tracim_backend.models.contents import GlobalStatus
11
+from tracim_backend.models.contents import CONTENT_STATUS
12
+from tracim_backend.models.contents import CONTENT_TYPES
10
 from tracim_backend.models.contents import open_status
13
 from tracim_backend.models.contents import open_status
11
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
12
-from tracim_backend.models.contents import ContentStatusLegacy as ContentStatus
13
 from tracim_backend.models.context_models import ActiveContentFilter
14
 from tracim_backend.models.context_models import ActiveContentFilter
15
+from tracim_backend.models.context_models import AutocompleteQuery
14
 from tracim_backend.models.context_models import ContentIdsQuery
16
 from tracim_backend.models.context_models import ContentIdsQuery
15
 from tracim_backend.models.context_models import UserWorkspaceAndContentPath
17
 from tracim_backend.models.context_models import UserWorkspaceAndContentPath
16
 from tracim_backend.models.context_models import ContentCreation
18
 from tracim_backend.models.context_models import ContentCreation
46
     user_id = marshmallow.fields.Int(dump_only=True, example=3)
48
     user_id = marshmallow.fields.Int(dump_only=True, example=3)
47
     avatar_url = marshmallow.fields.Url(
49
     avatar_url = marshmallow.fields.Url(
48
         allow_none=True,
50
         allow_none=True,
49
-        example="/api/v2/assets/avatars/suri-cate.jpg",
51
+        example="/api/v2/asset/avatars/suri-cate.jpg",
50
         description="avatar_url is the url to the image file. "
52
         description="avatar_url is the url to the image file. "
51
                     "If no avatar, then set it to null "
53
                     "If no avatar, then set it to null "
52
                     "(and frontend will interpret this with a default avatar)",
54
                     "(and frontend will interpret this with a default avatar)",
292
         return CommentPath(**data)
294
         return CommentPath(**data)
293
 
295
 
294
 
296
 
297
+class AutocompleteQuerySchema(marshmallow.Schema):
298
+    acp = marshmallow.fields.Str(
299
+        example='test',
300
+        description='search text to query',
301
+        validate=Length(min=2),
302
+    )
303
+    @post_load
304
+    def make_autocomplete(self, data):
305
+        return AutocompleteQuery(**data)
306
+
307
+
295
 class PageQuerySchema(marshmallow.Schema):
308
 class PageQuerySchema(marshmallow.Schema):
296
     page = marshmallow.fields.Int(
309
     page = marshmallow.fields.Int(
297
         example=2,
310
         example=2,
342
         validate=Range(min=0, max=1, error="Value must be 0 or 1"),
355
         validate=Range(min=0, max=1, error="Value must be 0 or 1"),
343
     )
356
     )
344
     content_type = marshmallow.fields.String(
357
     content_type = marshmallow.fields.String(
345
-        example=ContentType.Any,
346
-        default=ContentType.Any,
347
-        validate=OneOf(ContentType.allowed_type_values())
358
+        example=CONTENT_TYPES.Any_SLUG,
359
+        default=CONTENT_TYPES.Any_SLUG,
360
+        validate=OneOf(CONTENT_TYPES.endpoint_allowed_types_slug())
348
     )
361
     )
349
 
362
 
350
     @post_load
363
     @post_load
594
 class ContentTypeSchema(marshmallow.Schema):
607
 class ContentTypeSchema(marshmallow.Schema):
595
     slug = marshmallow.fields.String(
608
     slug = marshmallow.fields.String(
596
         example='pagehtml',
609
         example='pagehtml',
597
-        validate=OneOf(ContentType.allowed_types()),
610
+        validate=OneOf(CONTENT_TYPES.endpoint_allowed_types_slug()),
598
     )
611
     )
599
     fa_icon = marshmallow.fields.String(
612
     fa_icon = marshmallow.fields.String(
600
         example='fa-file-text-o',
613
         example='fa-file-text-o',
648
     )
661
     )
649
     content_type = marshmallow.fields.String(
662
     content_type = marshmallow.fields.String(
650
         example='html-document',
663
         example='html-document',
651
-        validate=OneOf(ContentType.allowed_types_for_folding()),  # nopep8
664
+        validate=OneOf(CONTENT_TYPES.endpoint_allowed_types_slug()),
652
     )
665
     )
653
     parent_id = marshmallow.fields.Integer(
666
     parent_id = marshmallow.fields.Integer(
654
         example=35,
667
         example=35,
683
     label = marshmallow.fields.Str(example='Intervention Report 12')
696
     label = marshmallow.fields.Str(example='Intervention Report 12')
684
     content_type = marshmallow.fields.Str(
697
     content_type = marshmallow.fields.Str(
685
         example='html-document',
698
         example='html-document',
686
-        validate=OneOf(ContentType.allowed_types()),
699
+        validate=OneOf(CONTENT_TYPES.endpoint_allowed_types_slug()),
687
     )
700
     )
688
     sub_content_types = marshmallow.fields.List(
701
     sub_content_types = marshmallow.fields.List(
689
         marshmallow.fields.String(
702
         marshmallow.fields.String(
690
             example='html-content',
703
             example='html-content',
691
-            validate=OneOf(ContentType.allowed_types())
704
+            validate=OneOf(CONTENT_TYPES.endpoint_allowed_types_slug())
692
         ),
705
         ),
693
         description='list of content types allowed as sub contents. '
706
         description='list of content types allowed as sub contents. '
694
                     'This field is required for folder contents, '
707
                     'This field is required for folder contents, '
696
     )
709
     )
697
     status = marshmallow.fields.Str(
710
     status = marshmallow.fields.Str(
698
         example='closed-deprecated',
711
         example='closed-deprecated',
699
-        validate=OneOf(ContentStatus.allowed_values()),
712
+        validate=OneOf(CONTENT_STATUS.get_all_slugs_values()),
700
         description='this slug is found in content_type available statuses',
713
         description='this slug is found in content_type available statuses',
701
         default=open_status
714
         default=open_status
702
     )
715
     )
721
 # Content
734
 # Content
722
 #####
735
 #####
723
 
736
 
737
+
724
 class ContentSchema(ContentDigestSchema):
738
 class ContentSchema(ContentDigestSchema):
725
     current_revision_id = marshmallow.fields.Int(example=12)
739
     current_revision_id = marshmallow.fields.Int(example=12)
726
     created = marshmallow.fields.DateTime(
740
     created = marshmallow.fields.DateTime(
840
 class SetContentStatusSchema(marshmallow.Schema):
854
 class SetContentStatusSchema(marshmallow.Schema):
841
     status = marshmallow.fields.Str(
855
     status = marshmallow.fields.Str(
842
         example='closed-deprecated',
856
         example='closed-deprecated',
843
-        validate=OneOf(ContentStatus.allowed_values()),
857
+        validate=OneOf(CONTENT_STATUS.get_all_slugs_values()),
844
         description='this slug is found in content_type available statuses',
858
         description='this slug is found in content_type available statuses',
845
         default=open_status,
859
         default=open_status,
846
         required=True,
860
         required=True,

+ 3 - 3
backend/tracim_backend/views/core_api/system_controller.py View File

5
 from tracim_backend.lib.utils.authorization import require_profile
5
 from tracim_backend.lib.utils.authorization import require_profile
6
 from tracim_backend.models import Group
6
 from tracim_backend.models import Group
7
 from tracim_backend.models.applications import applications
7
 from tracim_backend.models.applications import applications
8
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
8
+from tracim_backend.models.contents import CONTENT_TYPES
9
 
9
 
10
 try:  # Python 3.5+
10
 try:  # Python 3.5+
11
     from http import HTTPStatus
11
     from http import HTTPStatus
39
         """
39
         """
40
         Get list of alls content types availables in this tracim instance.
40
         Get list of alls content types availables in this tracim instance.
41
         """
41
         """
42
-        content_types_slugs = ContentType.allowed_types_for_folding()
43
-        content_types = [ContentType(slug) for slug in content_types_slugs]
42
+        content_types_slugs = CONTENT_TYPES.endpoint_allowed_types_slug()
43
+        content_types = [CONTENT_TYPES.get_one_by_slug(slug) for slug in content_types_slugs]
44
         return content_types
44
         return content_types
45
 
45
 
46
     def bind(self, configurator: Configurator) -> None:
46
     def bind(self, configurator: Configurator) -> None:

+ 59 - 2
backend/tracim_backend/views/core_api/user_controller.py View File

1
 from pyramid.config import Configurator
1
 from pyramid.config import Configurator
2
+
2
 try:  # Python 3.5+
3
 try:  # Python 3.5+
3
     from http import HTTPStatus
4
     from http import HTTPStatus
4
 except ImportError:
5
 except ImportError:
11
 from tracim_backend.lib.core.user import UserApi
12
 from tracim_backend.lib.core.user import UserApi
12
 from tracim_backend.lib.core.workspace import WorkspaceApi
13
 from tracim_backend.lib.core.workspace import WorkspaceApi
13
 from tracim_backend.lib.core.content import ContentApi
14
 from tracim_backend.lib.core.content import ContentApi
14
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
15
 from tracim_backend.views.controllers import Controller
15
 from tracim_backend.views.controllers import Controller
16
 from tracim_backend.lib.utils.authorization import require_same_user_or_profile
16
 from tracim_backend.lib.utils.authorization import require_same_user_or_profile
17
 from tracim_backend.lib.utils.authorization import require_profile
17
 from tracim_backend.lib.utils.authorization import require_profile
18
 from tracim_backend.exceptions import WrongUserPassword
18
 from tracim_backend.exceptions import WrongUserPassword
19
 from tracim_backend.exceptions import PasswordDoNotMatch
19
 from tracim_backend.exceptions import PasswordDoNotMatch
20
 from tracim_backend.views.core_api.schemas import UserSchema
20
 from tracim_backend.views.core_api.schemas import UserSchema
21
+from tracim_backend.views.core_api.schemas import AutocompleteQuerySchema
22
+from tracim_backend.views.core_api.schemas import UserDigestSchema
21
 from tracim_backend.views.core_api.schemas import SetEmailSchema
23
 from tracim_backend.views.core_api.schemas import SetEmailSchema
22
 from tracim_backend.views.core_api.schemas import SetPasswordSchema
24
 from tracim_backend.views.core_api.schemas import SetPasswordSchema
23
 from tracim_backend.views.core_api.schemas import UserInfosSchema
25
 from tracim_backend.views.core_api.schemas import UserInfosSchema
32
 from tracim_backend.views.core_api.schemas import ContentDigestSchema
34
 from tracim_backend.views.core_api.schemas import ContentDigestSchema
33
 from tracim_backend.views.core_api.schemas import ActiveContentFilterQuerySchema
35
 from tracim_backend.views.core_api.schemas import ActiveContentFilterQuerySchema
34
 from tracim_backend.views.core_api.schemas import WorkspaceDigestSchema
36
 from tracim_backend.views.core_api.schemas import WorkspaceDigestSchema
37
+from tracim_backend.models.contents import CONTENT_TYPES
35
 
38
 
36
 SWAGGER_TAG__USER_ENDPOINTS = 'Users'
39
 SWAGGER_TAG__USER_ENDPOINTS = 'Users'
37
 
40
 
76
         return uapi.get_user_with_context(request.candidate_user)
79
         return uapi.get_user_with_context(request.candidate_user)
77
 
80
 
78
     @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
81
     @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
82
+    @require_profile(Group.TIM_ADMIN)
83
+    @hapic.output_body(UserDigestSchema(many=True))
84
+    def users(self, context, request: TracimRequest, hapic_data=None):
85
+        """
86
+        Get all users
87
+        """
88
+        app_config = request.registry.settings['CFG']
89
+        uapi = UserApi(
90
+            current_user=request.current_user,  # User
91
+            session=request.dbsession,
92
+            config=app_config,
93
+        )
94
+        users = uapi.get_all()
95
+        context_users = [
96
+            uapi.get_user_with_context(user) for user in users
97
+        ]
98
+        return context_users
99
+
100
+    @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
101
+    @require_same_user_or_profile(Group.TIM_MANAGER)
102
+    @hapic.input_path(UserIdPathSchema())
103
+    @hapic.input_query(AutocompleteQuerySchema())
104
+    @hapic.output_body(UserDigestSchema(many=True))
105
+    def known_members(self, context, request: TracimRequest, hapic_data=None):
106
+        """
107
+        Get known users list
108
+        """
109
+        app_config = request.registry.settings['CFG']
110
+        uapi = UserApi(
111
+            current_user=request.candidate_user,  # User
112
+            session=request.dbsession,
113
+            config=app_config,
114
+        )
115
+        users = uapi.get_known_user(acp=hapic_data.query.acp)
116
+        context_users = [
117
+            uapi.get_user_with_context(user) for user in users
118
+        ]
119
+        return context_users
120
+
121
+    @hapic.with_api_doc(tags=[SWAGGER_TAG__USER_ENDPOINTS])
79
     @hapic.handle_exception(WrongUserPassword, HTTPStatus.FORBIDDEN)
122
     @hapic.handle_exception(WrongUserPassword, HTTPStatus.FORBIDDEN)
80
     @require_same_user_or_profile(Group.TIM_ADMIN)
123
     @require_same_user_or_profile(Group.TIM_ADMIN)
81
     @hapic.input_body(SetEmailSchema())
124
     @hapic.input_body(SetEmailSchema())
271
             before_content = api.get_one(
314
             before_content = api.get_one(
272
                 content_id=content_filter.before_content_id,
315
                 content_id=content_filter.before_content_id,
273
                 workspace=workspace,
316
                 workspace=workspace,
274
-                content_type=ContentType.Any
317
+                content_type=CONTENT_TYPES.Any_SLUG
275
             )
318
             )
276
         last_actives = api.get_last_active(
319
         last_actives = api.get_last_active(
277
             workspace=workspace,
320
             workspace=workspace,
328
         """
371
         """
329
         app_config = request.registry.settings['CFG']
372
         app_config = request.registry.settings['CFG']
330
         api = ContentApi(
373
         api = ContentApi(
374
+            show_archived=True,
375
+            show_deleted=True,
331
             current_user=request.candidate_user,
376
             current_user=request.candidate_user,
332
             session=request.dbsession,
377
             session=request.dbsession,
333
             config=app_config,
378
             config=app_config,
345
         """
390
         """
346
         app_config = request.registry.settings['CFG']
391
         app_config = request.registry.settings['CFG']
347
         api = ContentApi(
392
         api = ContentApi(
393
+            show_archived=True,
394
+            show_deleted=True,
348
             current_user=request.candidate_user,
395
             current_user=request.candidate_user,
349
             session=request.dbsession,
396
             session=request.dbsession,
350
             config=app_config,
397
             config=app_config,
362
         """
409
         """
363
         app_config = request.registry.settings['CFG']
410
         app_config = request.registry.settings['CFG']
364
         api = ContentApi(
411
         api = ContentApi(
412
+            show_archived=True,
413
+            show_deleted=True,
365
             current_user=request.candidate_user,
414
             current_user=request.candidate_user,
366
             session=request.dbsession,
415
             session=request.dbsession,
367
             config=app_config,
416
             config=app_config,
383
         configurator.add_route('user', '/users/{user_id}', request_method='GET')  # nopep8
432
         configurator.add_route('user', '/users/{user_id}', request_method='GET')  # nopep8
384
         configurator.add_view(self.user, route_name='user')
433
         configurator.add_view(self.user, route_name='user')
385
 
434
 
435
+        # users lists
436
+        configurator.add_route('users', '/users', request_method='GET')  # nopep8
437
+        configurator.add_view(self.users, route_name='users')
438
+
439
+        # known members lists
440
+        configurator.add_route('known_members', '/users/{user_id}/known_members', request_method='GET')  # nopep8
441
+        configurator.add_view(self.known_members, route_name='known_members')
442
+
386
         # set user email
443
         # set user email
387
         configurator.add_route('set_user_email', '/users/{user_id}/email', request_method='PUT')  # nopep8
444
         configurator.add_route('set_user_email', '/users/{user_id}/email', request_method='PUT')  # nopep8
388
         configurator.add_view(self.set_user_email, route_name='set_user_email')
445
         configurator.add_view(self.set_user_email, route_name='set_user_email')

+ 28 - 13
backend/tracim_backend/views/core_api/workspace_controller.py View File

31
 from tracim_backend.exceptions import WorkspacesDoNotMatch
31
 from tracim_backend.exceptions import WorkspacesDoNotMatch
32
 from tracim_backend.exceptions import ParentNotFound
32
 from tracim_backend.exceptions import ParentNotFound
33
 from tracim_backend.views.controllers import Controller
33
 from tracim_backend.views.controllers import Controller
34
+from tracim_backend.lib.utils.utils import password_generator
34
 from tracim_backend.views.core_api.schemas import FilterContentQuerySchema
35
 from tracim_backend.views.core_api.schemas import FilterContentQuerySchema
35
 from tracim_backend.views.core_api.schemas import WorkspaceMemberCreationSchema
36
 from tracim_backend.views.core_api.schemas import WorkspaceMemberCreationSchema
36
 from tracim_backend.views.core_api.schemas import WorkspaceMemberInviteSchema
37
 from tracim_backend.views.core_api.schemas import WorkspaceMemberInviteSchema
46
 from tracim_backend.views.core_api.schemas import WorkspaceSchema
47
 from tracim_backend.views.core_api.schemas import WorkspaceSchema
47
 from tracim_backend.views.core_api.schemas import WorkspaceIdPathSchema
48
 from tracim_backend.views.core_api.schemas import WorkspaceIdPathSchema
48
 from tracim_backend.views.core_api.schemas import WorkspaceMemberSchema
49
 from tracim_backend.views.core_api.schemas import WorkspaceMemberSchema
49
-from tracim_backend.models.contents import ContentTypeLegacy as ContentType
50
+from tracim_backend.models.contents import CONTENT_TYPES
50
 from tracim_backend.models.revision_protection import new_revision
51
 from tracim_backend.models.revision_protection import new_revision
51
 
52
 
52
 SWAGGER_TAG_WORKSPACE_ENDPOINTS = 'Workspaces'
53
 SWAGGER_TAG_WORKSPACE_ENDPOINTS = 'Workspaces'
213
                 # TODO - G.M - 2018-07-05 - [UserCreation] Reenable email
214
                 # TODO - G.M - 2018-07-05 - [UserCreation] Reenable email
214
                 # notification for creation
215
                 # notification for creation
215
                 user = uapi.create_user(
216
                 user = uapi.create_user(
216
-                    hapic_data.body.user_email_or_public_name,
217
-                    do_notify=False
217
+                    email=hapic_data.body.user_email_or_public_name,
218
+                    password= password_generator(),
219
+                    do_notify=True
218
                 )  # nopep8
220
                 )  # nopep8
219
                 newly_created = True
221
                 newly_created = True
222
+                if app_config.EMAIL_NOTIFICATION_ACTIVATED and \
223
+                        app_config.EMAIL_NOTIFICATION_PROCESSING_MODE.lower() == 'sync':
224
+                    email_sent = True
225
+
220
             except EmailValidationFailed:
226
             except EmailValidationFailed:
221
                 raise UserCreationFailed('no valid mail given')
227
                 raise UserCreationFailed('no valid mail given')
228
+
222
         role = rapi.create_one(
229
         role = rapi.create_one(
223
             user=user,
230
             user=user,
224
             workspace=request.current_workspace,
231
             workspace=request.current_workspace,
259
         contents = api.get_all(
266
         contents = api.get_all(
260
             parent_id=content_filter.parent_id,
267
             parent_id=content_filter.parent_id,
261
             workspace=request.current_workspace,
268
             workspace=request.current_workspace,
262
-            content_type=content_filter.content_type or ContentType.Any,
269
+            content_type=content_filter.content_type or CONTENT_TYPES.Any_SLUG,
263
         )
270
         )
264
         contents = [
271
         contents = [
265
             api.get_content_in_context(content) for content in contents
272
             api.get_content_in_context(content) for content in contents
291
         parent = None
298
         parent = None
292
         if creation_data.parent_id:
299
         if creation_data.parent_id:
293
             try:
300
             try:
294
-                parent = api.get_one(content_id=creation_data.parent_id, content_type=ContentType.Any)  # nopep8
301
+                parent = api.get_one(content_id=creation_data.parent_id, content_type=CONTENT_TYPES.Any_SLUG)  # nopep8
295
             except ContentNotFound as exc:
302
             except ContentNotFound as exc:
296
                 raise ParentNotFound(
303
                 raise ParentNotFound(
297
                     'Parent with content_id {} not found'.format(creation_data.parent_id)
304
                     'Parent with content_id {} not found'.format(creation_data.parent_id)
298
                 ) from exc
305
                 ) from exc
299
         content = api.create(
306
         content = api.create(
300
             label=creation_data.label,
307
             label=creation_data.label,
301
-            content_type=creation_data.content_type,
308
+            content_type_slug=creation_data.content_type,
302
             workspace=request.current_workspace,
309
             workspace=request.current_workspace,
303
             parent=parent,
310
             parent=parent,
304
         )
311
         )
327
         move_data = hapic_data.body
334
         move_data = hapic_data.body
328
 
335
 
329
         api = ContentApi(
336
         api = ContentApi(
337
+            show_archived=True,
338
+            show_deleted=True,
330
             current_user=request.current_user,
339
             current_user=request.current_user,
331
             session=request.dbsession,
340
             session=request.dbsession,
332
             config=app_config,
341
             config=app_config,
333
         )
342
         )
334
         content = api.get_one(
343
         content = api.get_one(
335
             path_data.content_id,
344
             path_data.content_id,
336
-            content_type=ContentType.Any
345
+            content_type=CONTENT_TYPES.Any_SLUG
337
         )
346
         )
338
         new_parent = api.get_one(
347
         new_parent = api.get_one(
339
-            move_data.new_parent_id, content_type=ContentType.Any
348
+            move_data.new_parent_id, content_type=CONTENT_TYPES.Any_SLUG
340
         )
349
         )
341
 
350
 
342
         new_workspace = request.candidate_workspace
351
         new_workspace = request.candidate_workspace
354
             )
363
             )
355
         updated_content = api.get_one(
364
         updated_content = api.get_one(
356
             path_data.content_id,
365
             path_data.content_id,
357
-            content_type=ContentType.Any
366
+            content_type=CONTENT_TYPES.Any_SLUG
358
         )
367
         )
359
         return api.get_content_in_context(updated_content)
368
         return api.get_content_in_context(updated_content)
360
 
369
 
374
         app_config = request.registry.settings['CFG']
383
         app_config = request.registry.settings['CFG']
375
         path_data = hapic_data.path
384
         path_data = hapic_data.path
376
         api = ContentApi(
385
         api = ContentApi(
386
+            show_archived=True,
387
+            show_deleted=True,
377
             current_user=request.current_user,
388
             current_user=request.current_user,
378
             session=request.dbsession,
389
             session=request.dbsession,
379
             config=app_config,
390
             config=app_config,
380
         )
391
         )
381
         content = api.get_one(
392
         content = api.get_one(
382
             path_data.content_id,
393
             path_data.content_id,
383
-            content_type=ContentType.Any
394
+            content_type=CONTENT_TYPES.Any_SLUG
384
         )
395
         )
385
         with new_revision(
396
         with new_revision(
386
                 session=request.dbsession,
397
                 session=request.dbsession,
410
             session=request.dbsession,
421
             session=request.dbsession,
411
             config=app_config,
422
             config=app_config,
412
             show_deleted=True,
423
             show_deleted=True,
424
+            show_archived=True,
413
         )
425
         )
414
         content = api.get_one(
426
         content = api.get_one(
415
             path_data.content_id,
427
             path_data.content_id,
416
-            content_type=ContentType.Any
428
+            content_type=CONTENT_TYPES.Any_SLUG
417
         )
429
         )
418
         with new_revision(
430
         with new_revision(
419
                 session=request.dbsession,
431
                 session=request.dbsession,
439
         app_config = request.registry.settings['CFG']
451
         app_config = request.registry.settings['CFG']
440
         path_data = hapic_data.path
452
         path_data = hapic_data.path
441
         api = ContentApi(
453
         api = ContentApi(
454
+            show_archived=True,
455
+            show_deleted=True,
442
             current_user=request.current_user,
456
             current_user=request.current_user,
443
             session=request.dbsession,
457
             session=request.dbsession,
444
             config=app_config,
458
             config=app_config,
445
         )
459
         )
446
-        content = api.get_one(path_data.content_id, content_type=ContentType.Any)  # nopep8
460
+        content = api.get_one(path_data.content_id, content_type=CONTENT_TYPES.Any_SLUG)  # nopep8
447
         with new_revision(
461
         with new_revision(
448
                 session=request.dbsession,
462
                 session=request.dbsession,
449
                 tm=transaction.manager,
463
                 tm=transaction.manager,
472
             session=request.dbsession,
486
             session=request.dbsession,
473
             config=app_config,
487
             config=app_config,
474
             show_archived=True,
488
             show_archived=True,
489
+            show_deleted=True,
475
         )
490
         )
476
         content = api.get_one(
491
         content = api.get_one(
477
             path_data.content_id,
492
             path_data.content_id,
478
-            content_type=ContentType.Any
493
+            content_type=CONTENT_TYPES.Any_SLUG
479
         )
494
         )
480
         with new_revision(
495
         with new_revision(
481
                 session=request.dbsession,
496
                 session=request.dbsession,

+ 67 - 0
backend/tracim_backend/views/frontend.py View File

1
+import os
2
+
3
+from pyramid.renderers import render_to_response
4
+from pyramid.config import Configurator
5
+from tracim_backend.exceptions import PageNotFound
6
+from tracim_backend.models.applications import applications
7
+from tracim_backend.views import BASE_API_V2
8
+from tracim_backend.lib.utils.request import TracimRequest
9
+from tracim_backend.views.controllers import Controller
10
+import spectra
11
+
12
+INDEX_PAGE_NAME = 'index.mak'
13
+APP_FRONTEND_PATH = 'app/{minislug}.app.js'
14
+
15
+
16
+class FrontendController(Controller):
17
+
18
+    def __init__(self, dist_folder_path: str):
19
+        self.dist_folder_path = dist_folder_path
20
+
21
+    def _get_index_file_path(self):
22
+        index_file_path = os.path.join(self.dist_folder_path, INDEX_PAGE_NAME)
23
+        if not os.path.exists(index_file_path):
24
+            raise FileNotFoundError()
25
+        return index_file_path
26
+
27
+    def not_found_view(self, context, request: TracimRequest):
28
+
29
+        if request.path.startswith(BASE_API_V2):
30
+            raise PageNotFound('{} is not a valid path'.format(request.path)) from context  # nopep8
31
+        return self.index(context, request)
32
+
33
+    def index(self, context, request: TracimRequest):
34
+        app_config = request.registry.settings['CFG']
35
+        # TODO - G.M - 2018-08-07 - Refactor autogen valid app list for frontend
36
+        frontend_apps = []
37
+        for app in applications:
38
+            app_frontend_path = APP_FRONTEND_PATH.replace('{minislug}',
39
+                                                          app.minislug)  # nopep8
40
+            app_path = os.path.join(self.dist_folder_path,
41
+                                    app_frontend_path)  # nopep8
42
+            if os.path.exists(app_path):
43
+                frontend_apps.append(app)
44
+        return render_to_response(
45
+            self._get_index_file_path(),
46
+            {
47
+                'colors': {
48
+                    'primary': spectra.html('#7d4e24'),
49
+                },
50
+                'applications': frontend_apps,
51
+            }
52
+        )
53
+
54
+    def bind(self, configurator: Configurator) -> None:
55
+
56
+        configurator.add_notfound_view(self.not_found_view)
57
+        # index.html for /index.html and /
58
+        configurator.add_route('root', '/', request_method='GET')
59
+        configurator.add_view(self.index, route_name='root')
60
+        configurator.add_route('index', INDEX_PAGE_NAME, request_method='GET')
61
+        configurator.add_view(self.index, route_name='index')
62
+
63
+        for dirname in os.listdir(self.dist_folder_path):
64
+            configurator.add_static_view(
65
+                name=dirname,
66
+                path=os.path.join(self.dist_folder_path, dirname)
67
+            )

+ 1 - 1
bash_library.sh View File

5
 NC='\033[0m' # No Color
5
 NC='\033[0m' # No Color
6
 
6
 
7
 function log {
7
 function log {
8
-    echo -e "\n${YELLOW}[$(date +'%H:%M:%S')]${BROWN} $ $1${NC}\n"
8
+    echo -e "\n${YELLOW}[$(date +'%H:%M:%S')]${BROWN} $ $1${NC}"
9
 }
9
 }

+ 40 - 51
build_full_frontend.sh View File

1
 #!/bin/bash
1
 #!/bin/bash
2
 
2
 
3
+# shellcheck disable=SC1091
3
 . bash_library.sh # source bash_library.sh
4
 . bash_library.sh # source bash_library.sh
4
 
5
 
5
 windoz=""
6
 windoz=""
7
     windoz="windoz"
8
     windoz="windoz"
8
 fi
9
 fi
9
 
10
 
10
-echo -e "\n${BROWN}/!\ ${NC}this script does not run 'npm install'\n${BROWN}/!\ ${NC}it also assumes your webpack dev server of frontend is running"
11
+echo -e "\n${BROWN}/!\ ${NC}this script does not run 'npm install'\n${BROWN}/!\ ${NC}"
11
 
12
 
12
-# Tracim Lib
13
-
14
-log "cd frontend_lib"
15
-cd frontend_lib
16
-log "npm run buildtracimlib$windoz"
17
-npm run buildtracimlib$windoz
18
-cd -
19
-
20
-# app Html Document
13
+# get the new sources
14
+git pull origin develop
21
 
15
 
22
-log "cd frontend_app_html-document"
23
-cd frontend_app_html-document
16
+# create folder frontend/dist/app/ if no exists
17
+if [ ! -d "frontend/dist/app/" ]; then
18
+  mkdir frontend/dist/app/
19
+fi
24
 
20
 
25
-log "npm run build$windoz # for frontend_app_html-document"
26
-npm run build$windoz
21
+# Tracim Lib
22
+(
23
+  log "build frontend_lib"
24
+  cd frontend_lib || exit
25
+  npm run buildtracimlib$windoz
26
+)
27
 
27
 
28
-log "cp dist/html-document.app.js"
29
-cp dist/html-document.app.js ../frontend/dist/app
30
 
28
 
31
-log "cp i18next.scanner/en/translation.json ../frontend/dist/app/tml-document_en_translation.json"
32
-cp i18next.scanner/en/translation.json ../frontend/dist/app/html-document_en_translation.json
29
+# app Html Document
30
+(
31
+  cd frontend_app_html-document || exit
32
+  ./build_html-document.sh
33
+)
33
 
34
 
34
-log "cp i18next.scanner/fr/translation.json ../frontend/dist/app/html-document_fr_translation.json"
35
-cp i18next.scanner/fr/translation.json ../frontend/dist/app/html-document_fr_translation.json
36
-cd -
37
 
35
 
38
 # app Thread
36
 # app Thread
37
+(
38
+  cd frontend_app_thread || exit
39
+  ./build_thread.sh
40
+)
39
 
41
 
40
-log "cd frontend_app_thread"
41
-cd frontend_app_thread
42
-
43
-log "npm run build$windoz # for frontend_app_thread"
44
-npm run build$windoz
45
 
42
 
46
-log "cp dist/thread.app.js"
47
-cp dist/thread.app.js ../frontend/dist/app
48
-
49
-log "cp i18next.scanner/en/translation.json ../frontend/dist/app/thread_en_translation.json"
50
-cp i18next.scanner/en/translation.json ../frontend/dist/app/thread_en_translation.json
51
-
52
-log "cp i18next.scanner/fr/translation.json ../frontend/dist/app/thread_fr_translation.json"
53
-cp i18next.scanner/fr/translation.json ../frontend/dist/app/thread_fr_translation.json
54
-cd -
43
+# app Workspace
44
+(
45
+  cd frontend_app_workspace || exit
46
+  ./build_workspace.sh
47
+)
55
 
48
 
56
 # app Admin Workspace User
49
 # app Admin Workspace User
57
-
58
-log "cd frontend_app_admin_workspace_user"
59
-cd frontend_app_admin_workspace_user
60
-
61
-log "npm run build$windoz # for frontend_app_thread"
62
-npm run build$windoz
63
-
64
-log "cp dist/admin_workspace_user.app.js"
65
-cp dist/admin_workspace_user.app.js ../frontend/dist/app
66
-
67
-log "cp i18next.scanner/en/translation.json ../frontend/dist/app/admin_workspace_user_en_translation.json"
68
-cp i18next.scanner/en/translation.json ../frontend/dist/app/admin_workspace_user_en_translation.json
69
-
70
-log "cp i18next.scanner/fr/translation.json ../frontend/dist/app/admin_workspace_user_fr_translation.json"
71
-cp i18next.scanner/fr/translation.json ../frontend/dist/app/admin_workspace_user_fr_translation.json
72
-cd -
50
+(
51
+  cd frontend_app_admin_workspace_user || exit
52
+  ./build_admin_workspace_user.sh
53
+)
54
+
55
+# build Tracim
56
+(
57
+  cd frontend || exit
58
+  npm run build
59
+)
60
+
61
+log "-- frontend build successful."

+ 6 - 0
color.json.sample View File

1
+{
2
+  "primary": "#7d4e24",
3
+  "html-document": "#3f52e3",
4
+  "thread": "#ad4cf9",
5
+  "file": "#ff9900"
6
+}

+ 3 - 1
frontend/.gitignore View File

1
 # Created by .ignore support plugin (hsz.mobi)
1
 # Created by .ignore support plugin (hsz.mobi)
2
-.idea/
3
 .git/
2
 .git/
4
 node_modules/
3
 node_modules/
4
+dist/asset/tracim.app.entry.js
5
+dist/asset/tracim.vendor.bundle.js
6
+dist/asset/images/

frontend/dist/dev/bootstrap-4.0.0-beta.2.js → frontend/dist/asset/bootstrap/bootstrap-4.0.0-beta.2.js View File


frontend/dist/dev/bootstrap-4.0.0-beta.css → frontend/dist/asset/bootstrap/bootstrap-4.0.0-beta.css View File


frontend/dist/dev/jquery-3.2.1.js → frontend/dist/asset/bootstrap/jquery-3.2.1.js View File


frontend/dist/dev/popper-1.12.3.js → frontend/dist/asset/bootstrap/popper-1.12.3.js View File


frontend/dist/font/font-awesome-4.7.0/HELP-US-OUT.txt → frontend/dist/asset/font/font-awesome-4.7.0/HELP-US-OUT.txt View File


frontend/dist/font/font-awesome-4.7.0/css/font-awesome.css → frontend/dist/asset/font/font-awesome-4.7.0/css/font-awesome.css View File


frontend/dist/font/font-awesome-4.7.0/css/font-awesome.min.css → frontend/dist/asset/font/font-awesome-4.7.0/css/font-awesome.min.css View File


frontend/dist/font/font-awesome-4.7.0/fonts/FontAwesome.otf → frontend/dist/asset/font/font-awesome-4.7.0/fonts/FontAwesome.otf View File


frontend/dist/font/font-awesome-4.7.0/fonts/fontawesome-webfont.eot → frontend/dist/asset/font/font-awesome-4.7.0/fonts/fontawesome-webfont.eot View File


frontend/dist/font/font-awesome-4.7.0/fonts/fontawesome-webfont.svg → frontend/dist/asset/font/font-awesome-4.7.0/fonts/fontawesome-webfont.svg View File


frontend/dist/font/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf → frontend/dist/asset/font/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf View File


frontend/dist/font/font-awesome-4.7.0/fonts/fontawesome-webfont.woff → frontend/dist/asset/font/font-awesome-4.7.0/fonts/fontawesome-webfont.woff View File


frontend/dist/font/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 → frontend/dist/asset/font/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 View File


frontend/dist/font/font-awesome-4.7.0/less/animated.less → frontend/dist/asset/font/font-awesome-4.7.0/less/animated.less View File


frontend/dist/font/font-awesome-4.7.0/less/bordered-pulled.less → frontend/dist/asset/font/font-awesome-4.7.0/less/bordered-pulled.less View File


frontend/dist/font/font-awesome-4.7.0/less/core.less → frontend/dist/asset/font/font-awesome-4.7.0/less/core.less View File


frontend/dist/font/font-awesome-4.7.0/less/fixed-width.less → frontend/dist/asset/font/font-awesome-4.7.0/less/fixed-width.less View File


frontend/dist/font/font-awesome-4.7.0/less/font-awesome.less → frontend/dist/asset/font/font-awesome-4.7.0/less/font-awesome.less View File


frontend/dist/font/font-awesome-4.7.0/less/icons.less → frontend/dist/asset/font/font-awesome-4.7.0/less/icons.less View File


frontend/dist/font/font-awesome-4.7.0/less/larger.less → frontend/dist/asset/font/font-awesome-4.7.0/less/larger.less View File


frontend/dist/font/font-awesome-4.7.0/less/list.less → frontend/dist/asset/font/font-awesome-4.7.0/less/list.less View File


frontend/dist/font/font-awesome-4.7.0/less/mixins.less → frontend/dist/asset/font/font-awesome-4.7.0/less/mixins.less View File


frontend/dist/font/font-awesome-4.7.0/less/path.less → frontend/dist/asset/font/font-awesome-4.7.0/less/path.less View File


frontend/dist/font/font-awesome-4.7.0/less/rotated-flipped.less → frontend/dist/asset/font/font-awesome-4.7.0/less/rotated-flipped.less View File


frontend/dist/font/font-awesome-4.7.0/less/screen-reader.less → frontend/dist/asset/font/font-awesome-4.7.0/less/screen-reader.less View File


frontend/dist/font/font-awesome-4.7.0/less/stacked.less → frontend/dist/asset/font/font-awesome-4.7.0/less/stacked.less View File


frontend/dist/font/font-awesome-4.7.0/less/variables.less → frontend/dist/asset/font/font-awesome-4.7.0/less/variables.less View File


frontend/dist/font/font-awesome-4.7.0/scss/_animated.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_animated.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/_bordered-pulled.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_bordered-pulled.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/_core.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_core.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/_fixed-width.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_fixed-width.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/_icons.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_icons.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/_larger.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_larger.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/_list.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_list.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/_mixins.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_mixins.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/_path.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_path.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/_rotated-flipped.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_rotated-flipped.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/_screen-reader.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_screen-reader.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/_stacked.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_stacked.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/_variables.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/_variables.scss View File


frontend/dist/font/font-awesome-4.7.0/scss/font-awesome.scss → frontend/dist/asset/font/font-awesome-4.7.0/scss/font-awesome.scss View File


frontend/dist/appInterface.js → frontend/dist/asset/tracim/appInterface.js View File

3
 
3
 
4
   getSelectedApp = name => {
4
   getSelectedApp = name => {
5
     switch (name) {
5
     switch (name) {
6
+      case 'workspace':
7
+        return appWorkspace
6
       case 'html-document':
8
       case 'html-document':
7
         return appHtmlDocument
9
         return appHtmlDocument
8
       case 'thread':
10
       case 'thread':
74
   GLOBAL_eventReducer = ({detail: {type, data}}) => {
76
   GLOBAL_eventReducer = ({detail: {type, data}}) => {
75
     switch (type) {
77
     switch (type) {
76
       case 'hide_popupCreateContent':
78
       case 'hide_popupCreateContent':
79
+      case 'hide_popupCreateWorkspace':
77
         console.log('%cGLOBAL_eventReducer Custom Event', 'color: #28a745', type, data)
80
         console.log('%cGLOBAL_eventReducer Custom Event', 'color: #28a745', type, data)
78
         getSelectedApp(data.name).unmountApp('popupCreateContentContainer')
81
         getSelectedApp(data.name).unmountApp('popupCreateContentContainer')
79
         break
82
         break

frontend/dist/tinymceInit.js → frontend/dist/asset/tracim/tinymceInit.js View File

32
     });
32
     });
33
 
33
 
34
     tinymce.init({
34
     tinymce.init({
35
+      forced_root_block : "",
35
       selector: selector,
36
       selector: selector,
36
       menubar: false,
37
       menubar: false,
37
       resize: false,
38
       resize: false,

BIN
frontend/dist/ecbb61e619a4d2801db1054c019316cc.jpg View File


+ 50 - 52
frontend/dist/index.html View File

1
 <!DOCTYPE html>
1
 <!DOCTYPE html>
2
 <html>
2
 <html>
3
-  <head>
4
-    <meta charset='utf-8' />
5
-    <meta name="viewport" content="width=device-width, user-scalable=no">
6
-    <title>Tracim</title>
7
-    <link rel='shortcut icon' type='image/x-icon' href='/asset/favicon.ico' >
3
+<head>
4
+  <meta charset='utf-8' />
5
+  <meta name="viewport" content="width=device-width, user-scalable=no">
6
+  <title>Tracim</title>
7
+  <link rel='shortcut icon' type='image/x-icon' href='/asset/favicon.ico' >
8
 
8
 
9
-    <link rel="stylesheet" type="text/css" href="/font/font-awesome-4.7.0/css/font-awesome.css">
10
-    <link href="https://fonts.googleapis.com/css?family=Quicksand:300,400,500,700" rel="stylesheet">
11
-    <!--
12
-    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
13
-    -->
14
-    <link rel="stylesheet" type="text/css" href="/asset/hamburger/hamburgers.min.css">
15
-    <link rel="stylesheet" type="text/css" href="/dev/bootstrap-4.0.0-beta.css">
9
+  <link rel="stylesheet" type="text/css" href="/asset/font/font-awesome-4.7.0/css/font-awesome.css">
10
+  <link href="https://fonts.googleapis.com/css?family=Quicksand:300,400,500,700" rel="stylesheet">
11
+  <!--
12
+  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
13
+  -->
14
+  <link rel="stylesheet" type="text/css" href="/asset/hamburger/hamburgers.min.css">
15
+  <link rel="stylesheet" type="text/css" href="/asset/bootstrap/bootstrap-4.0.0-beta.css">
16
 
16
 
17
-    <style>
18
-      /* code bellow will be generated by backend */
19
-      .primaryColorFont { color: #7d4e24; }
20
-      .primaryColorFontDarken { color: #572800; }
21
-      .primaryColorFontLighten { color: #a3744a; }
22
-      .whiteFontColor {color: #fdfdfd;}
17
+  <style>
18
+    .primaryColorFont { color: #7d4e24; }
19
+    .primaryColorFontDarken { color: #522c00; }
20
+    .primaryColorFontLighten { color: #a37346; }
23
 
21
 
24
-      .primaryColorFontHover:hover { color: #7d4e24; }
25
-      .primaryColorFontDarkenHover:hover { color: #572800; }
26
-      .primaryColorFontLightenHover:hover { color: #a3744a; }
22
+    .primaryColorFontHover:hover { color: #7d4e24; }
23
+    .primaryColorFontDarkenHover:hover { color: #522c00; }
24
+    .primaryColorFontLightenHover:hover { color: #a37346; }
27
 
25
 
28
-      .primaryColorBg { background-color: #7d4e24; }
29
-      .primaryColorBgDarken { background-color: #572800; }
30
-      .primaryColorBgLighten { background-color: #a3744a; }
31
 
26
 
32
-      .primaryColorBgHover:hover { background-color: #7d4e24; }
33
-      .primaryColorBgDarkenHover:hover { background-color: #572800; }
34
-      .primaryColorBgLightenHover:hover { background-color: #a3744a; }
27
+    .primaryColorBg { background-color: #7d4e24; }
28
+    .primaryColorBgDarken { background-color: #522c00; }
29
+    .primaryColorBgLighten { background-color: #a37346; }
35
 
30
 
36
-      .primaryColorBorder { border-color: #7d4e24; }
37
-      .primaryColorBorderDarken { border-color: #572800; }
38
-      .primaryColorBorderLighten { border-color: #a3744a; }
39
-      .whiteColorBorder { border-color: #fdfdfd; }
31
+    .primaryColorBgHover:hover { background-color: #7d4e24; }
32
+    .primaryColorBgDarkenHover:hover { background-color: #522c00; }
33
+    .primaryColorBgLightenHover:hover { background-color: #a37346; }
40
 
34
 
41
-      .primaryColorBorderHover:hover { border-color: #7d4e24; }
42
-      .primaryColorBorderDarkenHover:hover { border-color: #572800; }
43
-      .primaryColorBorderLightenHover:hover { border-color: #a3744a; }
44
 
35
 
45
-    </style>
46
-  </head>
36
+    .primaryColorBorder { border-color: #7d4e24; }
37
+    .primaryColorBorderDarken { border-color: #522c00; }
38
+    .primaryColorBorderLighten { border-color: #a37346; }
47
 
39
 
48
-  <body>
49
-    <div id='content'></div>
40
+    .primaryColorBorderHover:hover { border-color: #7d4e24; }
41
+    .primaryColorBorderDarkenHover:hover { border-color: #522c00; }
42
+    .primaryColorBorderLightenHover:hover { border-color: #a37346; }
43
+  </style>
44
+</head>
50
 
45
 
51
-    <script src='/tracim.vendor.bundle.js'></script>
52
-    <script src='/tracim.app.entry.js'></script>
46
+<body>
47
+<div id='content'></div>
53
 
48
 
54
-    <script src='/app/html-document.app.js'></script>
55
-    <script src='/app/thread.app.js'></script>
56
-    <!-- <script src='/app/file.app.js'></script> -->
57
-    <script src='/app/admin_workspace_user.app.js'></script>
49
+<script type='text/javascript' src='/asset/tracim.vendor.bundle.js'></script>
50
+<script type='text/javascript' src='/asset/tracim.app.entry.js'></script>
58
 
51
 
59
-    <script src="/dev/jquery-3.2.1.js"></script>
60
-    <script src="/dev/popper-1.12.3.js"></script>
61
-    <script src="/dev/bootstrap-4.0.0-beta.2.js"></script>
52
+<script type='text/javascript' src='/app/workspace.app.js'></script>
53
+<script type='text/javascript' src='/app/html-document.app.js'></script>
54
+<script type='text/javascript' src='/app/thread.app.js'></script>
55
+<script type='text/javascript' src='/app/admin_workspace_user.app.js'></script>
62
 
56
 
63
-    <script type="text/javascript" src="/asset/tinymce/js/tinymce/jquery.tinymce.min.js"></script>
64
-    <script type="text/javascript" src="/asset/tinymce/js/tinymce/tinymce.min.js"></script>
57
+<script type='text/javascript' src='/asset/bootstrap/jquery-3.2.1.js'></script>
58
+<script type='text/javascript' src='/asset/bootstrap/popper-1.12.3.js'></script>
59
+<script type='text/javascript' src='/asset/bootstrap/bootstrap-4.0.0-beta.2.js'></script>
65
 
60
 
66
-    <script type='text/javascript' src='/appInterface.js'></script>
67
-    <script type='text/javascript' src='/tinymceInit.js'></script>
68
-  </body>
61
+<script type='text/javascript' src='/asset/tinymce/js/tinymce/jquery.tinymce.min.js'></script>
62
+<script type='text/javascript' src='/asset/tinymce/js/tinymce/tinymce.min.js'></script>
63
+
64
+<script type='text/javascript' src='/asset/tracim/appInterface.js'></script>
65
+<script type='text/javascript' src='/asset/tracim/tinymceInit.js'></script>
66
+</body>
69
 </html>
67
 </html>

+ 0 - 0
frontend/dist/index.mak View File


Some files were not shown because too many files changed in this diff