Pārlūkot izejas kodu

Merge branch 'develop' of github.com:tracim/tracim_backend into feature/611_workspace_and_workspace_member_action_endpoints

Guénaël Muller 6 gadus atpakaļ
vecāks
revīzija
c542931ea4

+ 4 - 0
tracim/exceptions.py Parādīt failu

@@ -174,3 +174,7 @@ class EmailValidationFailed(TracimException):
174 174
 
175 175
 class UserCreationFailed(TracimException):
176 176
     pass
177
+
178
+
179
+class ParentNotFound(NotFound):
180
+    pass

+ 50 - 8
tracim/lib/webdav/dav_provider.py Parādīt failu

@@ -70,7 +70,12 @@ class Provider(DAVProvider):
70 70
 
71 71
         # If the requested path is the root, then we return a RootResource resource
72 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 80
         workspace_api = WorkspaceApi(
76 81
             current_user=user,
@@ -111,10 +116,24 @@ class Provider(DAVProvider):
111 116
 
112 117
         # Easy cases : path either end with /.deleted, /.archived or /.history, then we return corresponding resources
113 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 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 138
         if path.endswith(SpecialFolderExtension.History) and self._show_history:
120 139
             is_deleted_folder = re.search(r'/\.deleted/\.history$', path) is not None
@@ -124,7 +143,15 @@ class Provider(DAVProvider):
124 143
                 else HistoryType.Archived if is_archived_folder \
125 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 156
         # Now that's more complicated, we're trying to find out if the path end with /.history/file_name
130 157
         is_history_file_folder = re.search(r'/\.history/([^/]+)$', path) is not None
@@ -133,9 +160,10 @@ class Provider(DAVProvider):
133 160
             return resources.HistoryFileFolderResource(
134 161
                 path=path,
135 162
                 environ=environ,
136
-                content=content
163
+                user=user,
164
+                content=content,
165
+                session=session,
137 166
             )
138
-
139 167
         # And here next step :
140 168
         is_history_file = re.search(r'/\.history/[^/]+/\((\d+) - [a-zA-Z]+\) .+', path) is not None
141 169
 
@@ -147,9 +175,23 @@ class Provider(DAVProvider):
147 175
             content = self.get_content_from_revision(content_revision, content_api)
148 176
 
149 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 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 196
         # And if we're still going, the client is asking for a standard Folder/File/Page/Thread so we check the type7
155 197
         # and return the corresponding resource

+ 2 - 0
tracim/lib/webdav/resources.py Parādīt failu

@@ -516,6 +516,7 @@ class FolderResource(WorkspaceResource):
516 516
         workspace_api = WorkspaceApi(
517 517
             current_user=self.user,
518 518
             session=self.session,
519
+            config=self.provider.app_config,
519 520
         )
520 521
         workspace = self.provider.get_workspace_from_path(
521 522
             normpath(destpath), workspace_api
@@ -1308,6 +1309,7 @@ class FileResource(DAVNonCollection):
1308 1309
         workspace_api = WorkspaceApi(
1309 1310
             current_user=self.user,
1310 1311
             session=self.session,
1312
+            config=self.provider.app_config,
1311 1313
         )
1312 1314
         content_api = ContentApi(
1313 1315
             current_user=self.user,

+ 2 - 0
tracim/models/context_models.py Parādīt failu

@@ -134,9 +134,11 @@ class ContentCreation(object):
134 134
             self,
135 135
             label: str,
136 136
             content_type: str,
137
+            parent_id: typing.Optional[int] = None,
137 138
     ) -> None:
138 139
         self.label = label
139 140
         self.content_type = content_type
141
+        self.parent_id = parent_id
140 142
 
141 143
 
142 144
 class CommentCreation(object):

+ 43 - 0
tracim/tests/functional/test_workspaces.py Parādīt failu

@@ -1219,6 +1219,49 @@ class TestWorkspaceContents(FunctionalTest):
1219 1219
         active_contents = self.testapp.get('/api/v2/workspaces/1/contents', params=params_active, status=200).json_body  # nopep8
1220 1220
         assert res.json_body in active_contents
1221 1221
 
1222
+    def test_api__post_content_create_generic_content__ok_200__in_folder(self) -> None:  # nopep8
1223
+        """
1224
+        Create generic content in folder
1225
+        """
1226
+        self.testapp.authorization = (
1227
+            'Basic',
1228
+            (
1229
+                'admin@admin.admin',
1230
+                'admin@admin.admin'
1231
+            )
1232
+        )
1233
+        params = {
1234
+            'label': 'GenericCreatedContent',
1235
+            'content_type': 'markdownpage',
1236
+            'parent_id': 10,
1237
+        }
1238
+        res = self.testapp.post_json(
1239
+            '/api/v2/workspaces/1/contents',
1240
+            params=params,
1241
+            status=200
1242
+        )
1243
+        assert res
1244
+        assert res.json_body
1245
+        assert res.json_body['status'] == 'open'
1246
+        assert res.json_body['content_id']
1247
+        assert res.json_body['content_type'] == 'markdownpage'
1248
+        assert res.json_body['is_archived'] is False
1249
+        assert res.json_body['is_deleted'] is False
1250
+        assert res.json_body['workspace_id'] == 1
1251
+        assert res.json_body['slug'] == 'genericcreatedcontent'
1252
+        assert res.json_body['parent_id'] == 10
1253
+        assert res.json_body['show_in_ui'] is True
1254
+        assert res.json_body['sub_content_types']
1255
+        params_active = {
1256
+            'parent_id': 10,
1257
+            'show_archived': 0,
1258
+            'show_deleted': 0,
1259
+            'show_active': 1,
1260
+        }
1261
+        # INFO - G.M - 2018-06-165 - Verify if new content is correctly created
1262
+        active_contents = self.testapp.get('/api/v2/workspaces/1/contents', params=params_active, status=200).json_body  # nopep8
1263
+        assert res.json_body in active_contents
1264
+
1222 1265
     def test_api__post_content_create_generic_content__err_400__empty_label(self) -> None:  # nopep8
1223 1266
         """
1224 1267
         Create generic content

+ 5 - 0
tracim/views/core_api/schemas.py Parādīt failu

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

+ 12 - 1
tracim/views/core_api/workspace_controller.py Parādīt failu

@@ -27,7 +27,9 @@ from tracim.exceptions import EmptyLabelNotAllowed
27 27
 from tracim.exceptions import EmailValidationFailed
28 28
 from tracim.exceptions import UserCreationFailed
29 29
 from tracim.exceptions import UserDoesNotExist
30
+from tracim.exceptions import ContentNotFound
30 31
 from tracim.exceptions import WorkspacesDoNotMatch
32
+from tracim.exceptions import ParentNotFound
31 33
 from tracim.views.controllers import Controller
32 34
 from tracim.views.core_api.schemas import FilterContentQuerySchema
33 35
 from tracim.views.core_api.schemas import WorkspaceMemberCreationSchema
@@ -283,12 +285,21 @@ class WorkspaceController(Controller):
283 285
         api = ContentApi(
284 286
             current_user=request.current_user,
285 287
             session=request.dbsession,
286
-            config=app_config,
288
+            config=app_config
287 289
         )
290
+        parent = None
291
+        if creation_data.parent_id:
292
+            try:
293
+                parent = api.get_one(content_id=creation_data.parent_id, content_type=ContentType.Any)  # nopep8
294
+            except ContentNotFound as exc:
295
+                raise ParentNotFound(
296
+                    'Parent with content_id {} not found'.format(creation_data.parent_id)
297
+                ) from exc
288 298
         content = api.create(
289 299
             label=creation_data.label,
290 300
             content_type=creation_data.content_type,
291 301
             workspace=request.current_workspace,
302
+            parent=parent,
292 303
         )
293 304
         api.save(content, ActionDescription.CREATION)
294 305
         content = api.get_content_in_context(content)