Browse Source

Merge branch 'develop' of github.com:tracim/tracim_backend into feature/612_user_account_endpoint

Guénaël Muller 6 years ago
parent
commit
b87089e0b1

+ 4 - 0
tracim/exceptions.py View File

175
 
175
 
176
 class NoUserSetted(TracimException):
176
 class NoUserSetted(TracimException):
177
     pass
177
     pass
178
+
179
+
180
+class ParentNotFound(NotFound):
181
+    pass

+ 50 - 8
tracim/lib/webdav/dav_provider.py View File

70
 
70
 
71
         # If the requested path is the root, then we return a RootResource resource
71
         # If the requested path is the root, then we return a RootResource resource
72
         if path == root_path:
72
         if path == root_path:
73
-            return resources.RootResource(path, environ, user=user, session=session)
73
+            return resources.RootResource(
74
+                path=path,
75
+                environ=environ,
76
+                user=user,
77
+                session=session
78
+            )
74
 
79
 
75
         workspace_api = WorkspaceApi(
80
         workspace_api = WorkspaceApi(
76
             current_user=user,
81
             current_user=user,
111
 
116
 
112
         # Easy cases : path either end with /.deleted, /.archived or /.history, then we return corresponding resources
117
         # Easy cases : path either end with /.deleted, /.archived or /.history, then we return corresponding resources
113
         if path.endswith(SpecialFolderExtension.Archived) and self._show_archive:
118
         if path.endswith(SpecialFolderExtension.Archived) and self._show_archive:
114
-            return resources.ArchivedFolderResource(path, environ, workspace, content)
119
+            return resources.ArchivedFolderResource(
120
+                path=path,
121
+                environ=environ,
122
+                workspace=workspace,
123
+                user=user,
124
+                content=content,
125
+                session=session,
126
+            )
115
 
127
 
116
         if path.endswith(SpecialFolderExtension.Deleted) and self._show_delete:
128
         if path.endswith(SpecialFolderExtension.Deleted) and self._show_delete:
117
-            return resources.DeletedFolderResource(path, environ, workspace, content)
129
+            return resources.DeletedFolderResource(
130
+                path=path,
131
+                environ=environ,
132
+                workspace=workspace,
133
+                user=user,
134
+                content=content,
135
+                session=session,
136
+            )
118
 
137
 
119
         if path.endswith(SpecialFolderExtension.History) and self._show_history:
138
         if path.endswith(SpecialFolderExtension.History) and self._show_history:
120
             is_deleted_folder = re.search(r'/\.deleted/\.history$', path) is not None
139
             is_deleted_folder = re.search(r'/\.deleted/\.history$', path) is not None
124
                 else HistoryType.Archived if is_archived_folder \
143
                 else HistoryType.Archived if is_archived_folder \
125
                 else HistoryType.Standard
144
                 else HistoryType.Standard
126
 
145
 
127
-            return resources.HistoryFolderResource(path, environ, workspace, content, type)
146
+            return resources.HistoryFolderResource(
147
+                path=path,
148
+                environ=environ,
149
+                workspace=workspace,
150
+                user=user,
151
+                content=content,
152
+                session=session,
153
+                type=type
154
+            )
128
 
155
 
129
         # Now that's more complicated, we're trying to find out if the path end with /.history/file_name
156
         # Now that's more complicated, we're trying to find out if the path end with /.history/file_name
130
         is_history_file_folder = re.search(r'/\.history/([^/]+)$', path) is not None
157
         is_history_file_folder = re.search(r'/\.history/([^/]+)$', path) is not None
133
             return resources.HistoryFileFolderResource(
160
             return resources.HistoryFileFolderResource(
134
                 path=path,
161
                 path=path,
135
                 environ=environ,
162
                 environ=environ,
136
-                content=content
163
+                user=user,
164
+                content=content,
165
+                session=session,
137
             )
166
             )
138
-
139
         # And here next step :
167
         # And here next step :
140
         is_history_file = re.search(r'/\.history/[^/]+/\((\d+) - [a-zA-Z]+\) .+', path) is not None
168
         is_history_file = re.search(r'/\.history/[^/]+/\((\d+) - [a-zA-Z]+\) .+', path) is not None
141
 
169
 
147
             content = self.get_content_from_revision(content_revision, content_api)
175
             content = self.get_content_from_revision(content_revision, content_api)
148
 
176
 
149
             if content.type == ContentType.File:
177
             if content.type == ContentType.File:
150
-                return resources.HistoryFileResource(path, environ, content, content_revision)
178
+                return resources.HistoryFileResource(
179
+                    path=path,
180
+                    environ=environ,
181
+                    user=user,
182
+                    content=content,
183
+                    content_revision=content_revision,
184
+                    session=session,
185
+                )
151
             else:
186
             else:
152
-                return resources.HistoryOtherFile(path, environ, content, content_revision)
187
+                return resources.HistoryOtherFile(
188
+                    path=path,
189
+                    environ=environ,
190
+                    user=user,
191
+                    content=content,
192
+                    content_revision=content_revision,
193
+                    session=session,
194
+                )
153
 
195
 
154
         # And if we're still going, the client is asking for a standard Folder/File/Page/Thread so we check the type7
196
         # And if we're still going, the client is asking for a standard Folder/File/Page/Thread so we check the type7
155
         # and return the corresponding resource
197
         # and return the corresponding resource

+ 2 - 0
tracim/lib/webdav/resources.py View File

516
         workspace_api = WorkspaceApi(
516
         workspace_api = WorkspaceApi(
517
             current_user=self.user,
517
             current_user=self.user,
518
             session=self.session,
518
             session=self.session,
519
+            config=self.provider.app_config,
519
         )
520
         )
520
         workspace = self.provider.get_workspace_from_path(
521
         workspace = self.provider.get_workspace_from_path(
521
             normpath(destpath), workspace_api
522
             normpath(destpath), workspace_api
1308
         workspace_api = WorkspaceApi(
1309
         workspace_api = WorkspaceApi(
1309
             current_user=self.user,
1310
             current_user=self.user,
1310
             session=self.session,
1311
             session=self.session,
1312
+            config=self.provider.app_config,
1311
         )
1313
         )
1312
         content_api = ContentApi(
1314
         content_api = ContentApi(
1313
             current_user=self.user,
1315
             current_user=self.user,

+ 2 - 0
tracim/models/context_models.py View File

144
             self,
144
             self,
145
             label: str,
145
             label: str,
146
             content_type: str,
146
             content_type: str,
147
+            parent_id: typing.Optional[int] = None,
147
     ) -> None:
148
     ) -> None:
148
         self.label = label
149
         self.label = label
149
         self.content_type = content_type
150
         self.content_type = content_type
151
+        self.parent_id = parent_id
150
 
152
 
151
 
153
 
152
 class CommentCreation(object):
154
 class CommentCreation(object):

+ 43 - 0
tracim/tests/functional/test_workspaces.py View File

834
         active_contents = self.testapp.get('/api/v2/workspaces/1/contents', params=params_active, status=200).json_body  # nopep8
834
         active_contents = self.testapp.get('/api/v2/workspaces/1/contents', params=params_active, status=200).json_body  # nopep8
835
         assert res.json_body in active_contents
835
         assert res.json_body in active_contents
836
 
836
 
837
+    def test_api__post_content_create_generic_content__ok_200__in_folder(self) -> None:  # nopep8
838
+        """
839
+        Create generic content in folder
840
+        """
841
+        self.testapp.authorization = (
842
+            'Basic',
843
+            (
844
+                'admin@admin.admin',
845
+                'admin@admin.admin'
846
+            )
847
+        )
848
+        params = {
849
+            'label': 'GenericCreatedContent',
850
+            'content_type': 'markdownpage',
851
+            'parent_id': 10,
852
+        }
853
+        res = self.testapp.post_json(
854
+            '/api/v2/workspaces/1/contents',
855
+            params=params,
856
+            status=200
857
+        )
858
+        assert res
859
+        assert res.json_body
860
+        assert res.json_body['status'] == 'open'
861
+        assert res.json_body['content_id']
862
+        assert res.json_body['content_type'] == 'markdownpage'
863
+        assert res.json_body['is_archived'] is False
864
+        assert res.json_body['is_deleted'] is False
865
+        assert res.json_body['workspace_id'] == 1
866
+        assert res.json_body['slug'] == 'genericcreatedcontent'
867
+        assert res.json_body['parent_id'] == 10
868
+        assert res.json_body['show_in_ui'] is True
869
+        assert res.json_body['sub_content_types']
870
+        params_active = {
871
+            'parent_id': 10,
872
+            'show_archived': 0,
873
+            'show_deleted': 0,
874
+            'show_active': 1,
875
+        }
876
+        # INFO - G.M - 2018-06-165 - Verify if new content is correctly created
877
+        active_contents = self.testapp.get('/api/v2/workspaces/1/contents', params=params_active, status=200).json_body  # nopep8
878
+        assert res.json_body in active_contents
879
+
837
     def test_api__post_content_create_generic_content__err_400__empty_label(self) -> None:  # nopep8
880
     def test_api__post_content_create_generic_content__err_400__empty_label(self) -> None:  # nopep8
838
         """
881
         """
839
         Create generic content
882
         Create generic content

+ 5 - 0
tracim/views/core_api/schemas.py View File

447
         example='html-documents',
447
         example='html-documents',
448
         validate=OneOf(ContentType.allowed_types_for_folding()),  # nopep8
448
         validate=OneOf(ContentType.allowed_types_for_folding()),  # nopep8
449
     )
449
     )
450
+    parent_id = marshmallow.fields.Integer(
451
+        example=35,
452
+        description='content_id of parent content, if content should be placed in a folder, this should be folder content_id.'
453
+    )
454
+
450
 
455
 
451
     @post_load
456
     @post_load
452
     def make_content_filter(self, data):
457
     def make_content_filter(self, data):

+ 12 - 1
tracim/views/core_api/workspace_controller.py View File

18
 from tracim.models.context_models import UserRoleWorkspaceInContext
18
 from tracim.models.context_models import UserRoleWorkspaceInContext
19
 from tracim.models.context_models import ContentInContext
19
 from tracim.models.context_models import ContentInContext
20
 from tracim.exceptions import EmptyLabelNotAllowed
20
 from tracim.exceptions import EmptyLabelNotAllowed
21
+from tracim.exceptions import ContentNotFound
21
 from tracim.exceptions import WorkspacesDoNotMatch
22
 from tracim.exceptions import WorkspacesDoNotMatch
23
+from tracim.exceptions import ParentNotFound
22
 from tracim.views.controllers import Controller
24
 from tracim.views.controllers import Controller
23
 from tracim.views.core_api.schemas import FilterContentQuerySchema
25
 from tracim.views.core_api.schemas import FilterContentQuerySchema
24
 from tracim.views.core_api.schemas import ContentMoveSchema
26
 from tracim.views.core_api.schemas import ContentMoveSchema
133
         api = ContentApi(
135
         api = ContentApi(
134
             current_user=request.current_user,
136
             current_user=request.current_user,
135
             session=request.dbsession,
137
             session=request.dbsession,
136
-            config=app_config,
138
+            config=app_config
137
         )
139
         )
140
+        parent = None
141
+        if creation_data.parent_id:
142
+            try:
143
+                parent = api.get_one(content_id=creation_data.parent_id, content_type=ContentType.Any)  # nopep8
144
+            except ContentNotFound as exc:
145
+                raise ParentNotFound(
146
+                    'Parent with content_id {} not found'.format(creation_data.parent_id)
147
+                ) from exc
138
         content = api.create(
148
         content = api.create(
139
             label=creation_data.label,
149
             label=creation_data.label,
140
             content_type=creation_data.content_type,
150
             content_type=creation_data.content_type,
141
             workspace=request.current_workspace,
151
             workspace=request.current_workspace,
152
+            parent=parent,
142
         )
153
         )
143
         api.save(content, ActionDescription.CREATION)
154
         api.save(content, ActionDescription.CREATION)
144
         content = api.get_content_in_context(content)
155
         content = api.get_content_in_context(content)