|
@@ -23,8 +23,10 @@ from wsgidav.dav_provider import _DAVResource
|
23
|
23
|
|
24
|
24
|
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
|
25
|
25
|
|
26
|
|
-class Manage(object):
|
27
|
|
- """Objet qui sert à encapsuler l'exécution des actions de l'api archive/delete..."""
|
|
26
|
+class ManageActions(object):
|
|
27
|
+ """
|
|
28
|
+ This object is used to encapsulate all Deletion/Archiving related method as to not duplicate too much code
|
|
29
|
+ """
|
28
|
30
|
def __init__(self, action_type: str, api: ContentApi, content: Content):
|
29
|
31
|
self.content_api = api
|
30
|
32
|
self.content = content
|
|
@@ -46,10 +48,11 @@ class Manage(object):
|
46
|
48
|
|
47
|
49
|
def action(self):
|
48
|
50
|
try:
|
|
51
|
+ # When undeleting/unarchiving we except a content with the new name to not exist, thus if we
|
|
52
|
+ # don't get an error and the database request send back a result, we stop the action
|
49
|
53
|
self.content_api.get_one_by_label_and_parent(self._new_name, self.content.parent, self.content.workspace)
|
50
|
54
|
raise DAVError(HTTP_FORBIDDEN)
|
51
|
55
|
except NoResultFound:
|
52
|
|
- """Exécute l'action"""
|
53
|
56
|
with new_revision(self.content):
|
54
|
57
|
self.content_api.update_content(self.content, self._new_name)
|
55
|
58
|
self._actions[self._type](self.content)
|
|
@@ -57,13 +60,17 @@ class Manage(object):
|
57
|
60
|
|
58
|
61
|
transaction.commit()
|
59
|
62
|
|
60
|
|
- def make_name(self):
|
61
|
|
- """Créer le nouveau nom : rajoute de - archive date / retrait de - archive date suivant l'action"""
|
|
63
|
+ def make_name(self) -> str:
|
|
64
|
+ """
|
|
65
|
+ Will create the new name, either by adding '- deleted the [date]' after the name when archiving/deleting or
|
|
66
|
+ removing this string when undeleting/unarchiving
|
|
67
|
+ """
|
62
|
68
|
new_name = self.content.get_label()
|
63
|
69
|
extension = ''
|
64
|
|
- is_file_name = self.content.label == ''
|
65
|
|
-
|
66
|
70
|
|
|
71
|
+ # if the content has no label, the last .ext is important
|
|
72
|
+ # thus we want to rename a file from 'file.txt' to 'file - deleted... .txt' and not 'file.txt - deleted...'
|
|
73
|
+ is_file_name = self.content.label == ''
|
67
|
74
|
if is_file_name:
|
68
|
75
|
extension = re.search(r'(\.[^.]+)$', new_name).group(0)
|
69
|
76
|
new_name = re.sub(r'(\.[^.]+)$', '', new_name)
|
|
@@ -71,7 +78,11 @@ class Manage(object):
|
71
|
78
|
if self._type in [ActionDescription.ARCHIVING, ActionDescription.DELETION]:
|
72
|
79
|
new_name += ' - %s the %s' % (self._to_name[self._type], datetime.now().strftime('%d-%m-%Y at %H:%M'))
|
73
|
80
|
else:
|
74
|
|
- new_name = re.sub(r'( - (%s|%s) the .*)$' % (self._to_name[ActionDescription.DELETION], self._to_name[ActionDescription.ARCHIVING]), '', new_name)
|
|
81
|
+ new_name = re.sub(
|
|
82
|
+ r'( - (%s|%s) the .*)$' % (self._to_name[ActionDescription.DELETION], self._to_name[ActionDescription.ARCHIVING]),
|
|
83
|
+ '',
|
|
84
|
+ new_name
|
|
85
|
+ )
|
75
|
86
|
|
76
|
87
|
new_name += extension
|
77
|
88
|
|
|
@@ -79,7 +90,9 @@ class Manage(object):
|
79
|
90
|
|
80
|
91
|
|
81
|
92
|
class Root(DAVCollection):
|
82
|
|
- """Root ressource that represents tracim's home, which contains all workspaces"""
|
|
93
|
+ """
|
|
94
|
+ Root ressource that represents tracim's home, which contains all workspaces
|
|
95
|
+ """
|
83
|
96
|
|
84
|
97
|
def __init__(self, path: str, environ: dict):
|
85
|
98
|
super(Root, self).__init__(path, environ)
|
|
@@ -93,12 +106,16 @@ class Root(DAVCollection):
|
93
|
106
|
def getMemberNames(self) -> [str]:
|
94
|
107
|
"""
|
95
|
108
|
This method returns the names (here workspace's labels) of all its children
|
|
109
|
+
|
|
110
|
+ Though for perfomance issue, we're not using this function anymore
|
96
|
111
|
"""
|
97
|
112
|
return [workspace.label for workspace in self.workspace_api.get_all()]
|
98
|
113
|
|
99
|
114
|
def getMember(self, label: str) -> DAVCollection:
|
100
|
115
|
"""
|
101
|
116
|
This method returns the child Workspace that corresponds to a given name
|
|
117
|
+
|
|
118
|
+ Though for perfomance issue, we're not using this function anymore
|
102
|
119
|
"""
|
103
|
120
|
try:
|
104
|
121
|
workspace = self.workspace_api.get_one_by_label(label)
|
|
@@ -113,7 +130,8 @@ class Root(DAVCollection):
|
113
|
130
|
This method is called whenever the user wants to create a DAVNonCollection resource (files in our case).
|
114
|
131
|
|
115
|
132
|
There we don't allow to create files at the root;
|
116
|
|
- only workspaces (thus collection) can be created."""
|
|
133
|
+ only workspaces (thus collection) can be created.
|
|
134
|
+ """
|
117
|
135
|
raise DAVError(HTTP_FORBIDDEN)
|
118
|
136
|
|
119
|
137
|
def createCollection(self, name: str):
|
|
@@ -123,7 +141,8 @@ class Root(DAVCollection):
|
123
|
141
|
|
124
|
142
|
[For now] we don't allow to create new workspaces through
|
125
|
143
|
webdav client. Though if we come to allow it, deleting the error's raise will
|
126
|
|
- make it possible."""
|
|
144
|
+ make it possible.
|
|
145
|
+ """
|
127
|
146
|
# TODO : remove comment here
|
128
|
147
|
# raise DAVError(HTTP_FORBIDDEN)
|
129
|
148
|
|
|
@@ -137,8 +156,10 @@ class Root(DAVCollection):
|
137
|
156
|
return Workspace(workspace_path, self.environ, new_workspace)
|
138
|
157
|
|
139
|
158
|
def getMemberList(self):
|
140
|
|
- # De base on appellerait getMemberNames puis getMember, mais ça fait trop de requête alors on optimise :
|
141
|
|
- # on fait une seule requête en BDD; get_all et voilà !
|
|
159
|
+ """
|
|
160
|
+ This method is called by wsgidav when requesting with a depth > 0, it will return a list of _DAVResource
|
|
161
|
+ of all its direct children
|
|
162
|
+ """
|
142
|
163
|
|
143
|
164
|
members = []
|
144
|
165
|
for workspace in self.workspace_api.get_all():
|
|
@@ -149,8 +170,10 @@ class Root(DAVCollection):
|
149
|
170
|
|
150
|
171
|
|
151
|
172
|
class Workspace(DAVCollection):
|
152
|
|
- """Workspace resource corresponding to tracim's workspaces.
|
153
|
|
- Direct children can only be folders, though files might come later on"""
|
|
173
|
+ """
|
|
174
|
+ Workspace resource corresponding to tracim's workspaces.
|
|
175
|
+ Direct children can only be folders, though files might come later on and are supported
|
|
176
|
+ """
|
154
|
177
|
|
155
|
178
|
def __init__(self, path: str, environ: dict, workspace: data.Workspace):
|
156
|
179
|
super(Workspace, self).__init__(path, environ)
|
|
@@ -202,8 +225,10 @@ class Workspace(DAVCollection):
|
202
|
225
|
)
|
203
|
226
|
|
204
|
227
|
def createEmptyResource(self, file_name: str):
|
205
|
|
- """[For now] we don't allow to create files right under workspaces.
|
206
|
|
- Though if we come to allow it, deleting the error's raise will make it possible."""
|
|
228
|
+ """
|
|
229
|
+ [For now] we don't allow to create files right under workspaces.
|
|
230
|
+ Though if we come to allow it, deleting the error's raise will make it possible.
|
|
231
|
+ """
|
207
|
232
|
# TODO : remove commentary here raise DAVError(HTTP_FORBIDDEN)
|
208
|
233
|
if '/.deleted/' in self.path or '/.archived/' in self.path:
|
209
|
234
|
raise DAVError(HTTP_FORBIDDEN)
|
|
@@ -213,14 +238,17 @@ class Workspace(DAVCollection):
|
213
|
238
|
content_api=self.content_api,
|
214
|
239
|
workspace=self.workspace,
|
215
|
240
|
content=None,
|
216
|
|
- parent=self.content
|
|
241
|
+ parent=self.content,
|
|
242
|
+ path=self.path + '/' + file_name
|
217
|
243
|
)
|
218
|
244
|
|
219
|
245
|
def createCollection(self, label: str) -> 'Folder':
|
220
|
|
- """Create a new folder for the current workspace. As it's not possible for the user to choose
|
|
246
|
+ """
|
|
247
|
+ Create a new folder for the current workspace. As it's not possible for the user to choose
|
221
|
248
|
which types of content are allowed in this folder, we allow allow all of them.
|
222
|
249
|
|
223
|
|
- This method return the DAVCollection created."""
|
|
250
|
+ This method return the DAVCollection created.
|
|
251
|
+ """
|
224
|
252
|
|
225
|
253
|
if '/.deleted/' in self.path or '/.archived/' in self.path:
|
226
|
254
|
raise DAVError(HTTP_FORBIDDEN)
|
|
@@ -256,7 +284,7 @@ class Workspace(DAVCollection):
|
256
|
284
|
return True
|
257
|
285
|
|
258
|
286
|
def moveRecursive(self, destpath):
|
259
|
|
- if dirname(normpath(destpath)) == self.provider.root:
|
|
287
|
+ if dirname(normpath(destpath)) == self.environ['http_authenticator.realm']:
|
260
|
288
|
self.workspace.label = basename(normpath(destpath))
|
261
|
289
|
transaction.commit()
|
262
|
290
|
else:
|
|
@@ -264,15 +292,14 @@ class Workspace(DAVCollection):
|
264
|
292
|
|
265
|
293
|
def getMemberList(self) -> [_DAVResource]:
|
266
|
294
|
members = []
|
267
|
|
- parent_id = None if self.content is None else self.content.id
|
268
|
295
|
|
269
|
|
- childs = self.content_api.get_all(parent_id, ContentType.Any, self.workspace)
|
|
296
|
+ children = self.content_api.get_all(None, ContentType.Any, self.workspace)
|
270
|
297
|
|
271
|
|
- for content in childs:
|
|
298
|
+ for content in children:
|
272
|
299
|
content_path = '%s/%s' % (self.path, self.provider.transform_to_display(content.get_label()))
|
273
|
300
|
|
274
|
301
|
if content.type == ContentType.Folder:
|
275
|
|
- members.append(Folder(content_path, self.environ, content, self.workspace))
|
|
302
|
+ members.append(Folder(content_path, self.environ, self.workspace, content))
|
276
|
303
|
elif content.type == ContentType.File:
|
277
|
304
|
self._file_count += 1
|
278
|
305
|
members.append(File(content_path, self.environ, content))
|
|
@@ -310,15 +337,18 @@ class Workspace(DAVCollection):
|
310
|
337
|
workspace=self.workspace
|
311
|
338
|
)
|
312
|
339
|
)
|
|
340
|
+
|
313
|
341
|
return members
|
314
|
342
|
|
315
|
343
|
|
316
|
344
|
class Folder(Workspace):
|
317
|
|
- """Folder resource corresponding to tracim's folders.
|
|
345
|
+ """
|
|
346
|
+ Folder resource corresponding to tracim's folders.
|
318
|
347
|
Direct children can only be either folder, files, pages or threads
|
319
|
|
- By default when creating new folders, we allow them to contain all types of content"""
|
|
348
|
+ By default when creating new folders, we allow them to contain all types of content
|
|
349
|
+ """
|
320
|
350
|
|
321
|
|
- def __init__(self, path: str, environ: dict, content: data.Content, workspace: data.Workspace):
|
|
351
|
+ def __init__(self, path: str, environ: dict, workspace: data.Workspace, content: data.Content):
|
322
|
352
|
super(Folder, self).__init__(path, environ, workspace)
|
323
|
353
|
|
324
|
354
|
self.content = content
|
|
@@ -336,14 +366,16 @@ class Folder(Workspace):
|
336
|
366
|
return mktime(self.content.updated.timetuple())
|
337
|
367
|
|
338
|
368
|
def delete(self):
|
339
|
|
- Manage(ActionDescription.DELETION, self.content_api, self.content).action()
|
|
369
|
+ ManageActions(ActionDescription.DELETION, self.content_api, self.content).action()
|
340
|
370
|
|
341
|
371
|
def supportRecursiveMove(self, destpath: str):
|
342
|
372
|
return True
|
343
|
373
|
|
344
|
374
|
def moveRecursive(self, destpath: str):
|
345
|
|
- """As we support recursive move, copymovesingle won't be called, though with copy it'll be called
|
346
|
|
- but i have to check if the client ever call that function..."""
|
|
375
|
+ """
|
|
376
|
+ As we support recursive move, copymovesingle won't be called, though with copy it'll be called
|
|
377
|
+ but i have to check if the client ever call that function...
|
|
378
|
+ """
|
347
|
379
|
destpath = normpath(destpath)
|
348
|
380
|
|
349
|
381
|
invalid_path = False
|
|
@@ -357,7 +389,7 @@ class Folder(Workspace):
|
357
|
389
|
current_path = re.sub(r'/\.(deleted|archived)', '', self.path)
|
358
|
390
|
|
359
|
391
|
if current_path == destpath:
|
360
|
|
- Manage(
|
|
392
|
+ ManageActions(
|
361
|
393
|
ActionDescription.UNDELETION if self.content.is_deleted else ActionDescription.UNARCHIVING,
|
362
|
394
|
self.content_api,
|
363
|
395
|
self.content
|
|
@@ -371,7 +403,7 @@ class Folder(Workspace):
|
371
|
403
|
dest_path = re.sub(r'/\.(deleted|archived)', '', destpath)
|
372
|
404
|
|
373
|
405
|
if dest_path == self.path:
|
374
|
|
- Manage(
|
|
406
|
+ ManageActions(
|
375
|
407
|
ActionDescription.DELETION if '.deleted' in destpath else ActionDescription.ARCHIVING,
|
376
|
408
|
self.content_api,
|
377
|
409
|
self.content
|
|
@@ -392,15 +424,16 @@ class Folder(Workspace):
|
392
|
424
|
raise DAVError(HTTP_FORBIDDEN)
|
393
|
425
|
|
394
|
426
|
def move_folder(self, destpath):
|
395
|
|
- parent = self.provider.get_parent_from_path(
|
396
|
|
- normpath(destpath),
|
397
|
|
- self.content_api,
|
398
|
|
- WorkspaceApi(self.user)
|
399
|
|
- )
|
400
|
427
|
|
|
428
|
+ workspace_api = WorkspaceApi(self.user)
|
401
|
429
|
workspace = self.provider.get_workspace_from_path(
|
|
430
|
+ normpath(destpath), workspace_api
|
|
431
|
+ )
|
|
432
|
+
|
|
433
|
+ parent = self.provider.get_parent_from_path(
|
402
|
434
|
normpath(destpath),
|
403
|
|
- WorkspaceApi(self.user)
|
|
435
|
+ self.content_api,
|
|
436
|
+ workspace
|
404
|
437
|
)
|
405
|
438
|
|
406
|
439
|
with new_revision(self.content):
|
|
@@ -408,28 +441,71 @@ class Folder(Workspace):
|
408
|
441
|
self.content_api.update_content(self.content, self.provider.transform_to_bdd(basename(destpath)))
|
409
|
442
|
self.content_api.save(self.content)
|
410
|
443
|
else:
|
411
|
|
- try:
|
412
|
|
- workspace_id = parent.workspace.workspace_id
|
413
|
|
- except AttributeError:
|
414
|
|
- workspace_id = self.provider.get_workspace_from_path(
|
415
|
|
- destpath, WorkspaceApi(self.user)
|
416
|
|
- ).workspace_id
|
417
|
|
-
|
418
|
|
- if workspace_id == self.content.workspace.workspace_id:
|
|
444
|
+ if workspace.workspace_id == self.content.workspace.workspace_id:
|
419
|
445
|
self.content_api.move(self.content, parent)
|
420
|
446
|
else:
|
421
|
|
- try:
|
422
|
|
- self.content_api.move_recursively(self.content, parent, parent.workspace)
|
423
|
|
- except AttributeError:
|
424
|
|
- self.content_api.move_recursively(self.content, parent, workspace)
|
|
447
|
+ self.content_api.move_recursively(self.content, parent, workspace)
|
425
|
448
|
|
426
|
449
|
transaction.commit()
|
427
|
450
|
|
|
451
|
+ def getMemberList(self) -> [_DAVResource]:
|
|
452
|
+ members = []
|
|
453
|
+
|
|
454
|
+ for content in self.content.children:
|
|
455
|
+ content_path = '%s/%s' % (self.path, self.provider.transform_to_display(content.get_label()))
|
428
|
456
|
|
429
|
|
-class HistoryFolder(Folder):
|
|
457
|
+ if content.type == ContentType.Folder:
|
|
458
|
+ members.append(Folder(content_path, self.environ, self.workspace, content))
|
|
459
|
+ elif content.type == ContentType.File:
|
|
460
|
+ self._file_count += 1
|
|
461
|
+ members.append(File(content_path, self.environ, content))
|
|
462
|
+ else:
|
|
463
|
+ self._file_count += 1
|
|
464
|
+ members.append(OtherFile(content_path, self.environ, content))
|
|
465
|
+
|
|
466
|
+ if self._file_count > 0 and self.provider.show_history():
|
|
467
|
+ members.append(
|
|
468
|
+ HistoryFolder(
|
|
469
|
+ path=self.path + '/' + ".history",
|
|
470
|
+ environ=self.environ,
|
|
471
|
+ content=self.content,
|
|
472
|
+ workspace=self.workspace,
|
|
473
|
+ type=HistoryType.Standard
|
|
474
|
+ )
|
|
475
|
+ )
|
|
476
|
+
|
|
477
|
+ if self.provider.show_delete():
|
|
478
|
+ members.append(
|
|
479
|
+ DeletedFolder(
|
|
480
|
+ path=self.path + '/' + ".deleted",
|
|
481
|
+ environ=self.environ,
|
|
482
|
+ content=self.content,
|
|
483
|
+ workspace=self.workspace
|
|
484
|
+ )
|
|
485
|
+ )
|
|
486
|
+
|
|
487
|
+ if self.provider.show_archive():
|
|
488
|
+ members.append(
|
|
489
|
+ ArchivedFolder(
|
|
490
|
+ path=self.path + '/' + ".archived",
|
|
491
|
+ environ=self.environ,
|
|
492
|
+ content=self.content,
|
|
493
|
+ workspace=self.workspace
|
|
494
|
+ )
|
|
495
|
+ )
|
|
496
|
+
|
|
497
|
+ return members
|
430
|
498
|
|
431
|
|
- def __init__(self, path, environ, workspace: data.Workspace, type: str, content: data.Content=None):
|
432
|
|
- super(HistoryFolder, self).__init__(path, environ, content, workspace)
|
|
499
|
+
|
|
500
|
+class HistoryFolder(Folder):
|
|
501
|
+ """
|
|
502
|
+ A virtual resource which contains a sub-folder for every files (DAVNonCollection) contained in the parent
|
|
503
|
+ folder
|
|
504
|
+ """
|
|
505
|
+
|
|
506
|
+ def __init__(self, path, environ, workspace: data.Workspace,
|
|
507
|
+ content: data.Content=None, type: str=HistoryType.Standard):
|
|
508
|
+ super(HistoryFolder, self).__init__(path, environ, workspace, content)
|
433
|
509
|
|
434
|
510
|
self._is_archived = type == HistoryType.Archived
|
435
|
511
|
self._is_deleted = type == HistoryType.Deleted
|
|
@@ -496,10 +572,13 @@ class HistoryFolder(Folder):
|
496
|
572
|
|
497
|
573
|
def getMemberList(self) -> [_DAVResource]:
|
498
|
574
|
members = []
|
499
|
|
-
|
500
|
|
- parent_id = None if self.content is None else self.content.id
|
501
|
|
-
|
502
|
|
- for content in self.content_api.get_all_with_filter(parent_id, ContentType.Any, self.workspace):
|
|
575
|
+
|
|
576
|
+ if self.content:
|
|
577
|
+ children = self.content.children
|
|
578
|
+ else:
|
|
579
|
+ children = self.content_api.get_all(None, ContentType.Any, self.workspace)
|
|
580
|
+
|
|
581
|
+ for content in children:
|
503
|
582
|
members.append(HistoryFileFolder(
|
504
|
583
|
path='%s/%s' % (self.path, content.get_label()),
|
505
|
584
|
environ=self.environ,
|
|
@@ -509,8 +588,12 @@ class HistoryFolder(Folder):
|
509
|
588
|
|
510
|
589
|
|
511
|
590
|
class DeletedFolder(HistoryFolder):
|
|
591
|
+ """
|
|
592
|
+ A virtual resources which exists for every folder or workspaces which contains their deleted children
|
|
593
|
+ """
|
|
594
|
+
|
512
|
595
|
def __init__(self, path: str, environ: dict, workspace: data.Workspace, content: data.Content=None):
|
513
|
|
- super(DeletedFolder, self).__init__(path, environ, workspace, HistoryType.Deleted, content)
|
|
596
|
+ super(DeletedFolder, self).__init__(path, environ, workspace, content, HistoryType.Deleted)
|
514
|
597
|
|
515
|
598
|
self._file_count = 0
|
516
|
599
|
|
|
@@ -541,8 +624,12 @@ class DeletedFolder(HistoryFolder):
|
541
|
624
|
def getMemberNames(self) -> [str]:
|
542
|
625
|
retlist = []
|
543
|
626
|
|
544
|
|
- for content in self.content_api.get_all(
|
545
|
|
- parent_id=self.content if self.content is None else self.content.id, content_type=ContentType.Any):
|
|
627
|
+ if self.content:
|
|
628
|
+ children = self.content.children
|
|
629
|
+ else:
|
|
630
|
+ children = self.content_api.get_all(None, ContentType.Any, self.workspace)
|
|
631
|
+
|
|
632
|
+ for content in children:
|
546
|
633
|
if content.is_deleted:
|
547
|
634
|
retlist.append(content.get_label())
|
548
|
635
|
|
|
@@ -551,25 +638,19 @@ class DeletedFolder(HistoryFolder):
|
551
|
638
|
|
552
|
639
|
return retlist
|
553
|
640
|
|
554
|
|
- def createEmptyResource(self, name: str):
|
555
|
|
- raise DAVError(HTTP_FORBIDDEN)
|
556
|
|
-
|
557
|
|
- def createCollection(self, name: str):
|
558
|
|
- raise DAVError(HTTP_FORBIDDEN)
|
559
|
|
-
|
560
|
|
- def delete(self):
|
561
|
|
- raise DAVError(HTTP_FORBIDDEN)
|
562
|
|
-
|
563
|
641
|
def getMemberList(self) -> [_DAVResource]:
|
564
|
642
|
members = []
|
565
|
643
|
|
566
|
|
- parent_id = None if self.content is None else self.content.id
|
|
644
|
+ if self.content:
|
|
645
|
+ children = self.content.children
|
|
646
|
+ else:
|
|
647
|
+ children = self.content_api.get_all(None, ContentType.Any, self.workspace)
|
567
|
648
|
|
568
|
|
- for content in self.content_api.get_all_with_filter(parent_id, ContentType.Any, self.workspace):
|
|
649
|
+ for content in children:
|
569
|
650
|
content_path = '%s/%s' % (self.path, self.provider.transform_to_display(content.get_label()))
|
570
|
651
|
|
571
|
652
|
if content.type == ContentType.Folder:
|
572
|
|
- members.append(Folder(content_path, self.environ, content, self.workspace))
|
|
653
|
+ members.append(Folder(content_path, self.environ, self.workspace, content))
|
573
|
654
|
elif content.type == ContentType.File:
|
574
|
655
|
self._file_count += 1
|
575
|
656
|
members.append(File(content_path, self.environ, content))
|
|
@@ -592,8 +673,11 @@ class DeletedFolder(HistoryFolder):
|
592
|
673
|
|
593
|
674
|
|
594
|
675
|
class ArchivedFolder(HistoryFolder):
|
|
676
|
+ """
|
|
677
|
+ A virtual resources which exists for every folder or workspaces which contains their archived children
|
|
678
|
+ """
|
595
|
679
|
def __init__(self, path: str, environ: dict, workspace: data.Workspace, content: data.Content=None):
|
596
|
|
- super(ArchivedFolder, self).__init__(path, environ, workspace, HistoryType.Archived, content)
|
|
680
|
+ super(ArchivedFolder, self).__init__(path, environ, workspace, content, HistoryType.Archived)
|
597
|
681
|
|
598
|
682
|
self._file_count = 0
|
599
|
683
|
|
|
@@ -633,25 +717,19 @@ class ArchivedFolder(HistoryFolder):
|
633
|
717
|
|
634
|
718
|
return retlist
|
635
|
719
|
|
636
|
|
- def createEmptyResource(self, name):
|
637
|
|
- raise DAVError(HTTP_FORBIDDEN)
|
638
|
|
-
|
639
|
|
- def createCollection(self, name):
|
640
|
|
- raise DAVError(HTTP_FORBIDDEN)
|
641
|
|
-
|
642
|
|
- def delete(self):
|
643
|
|
- raise DAVError(HTTP_FORBIDDEN)
|
644
|
|
-
|
645
|
720
|
def getMemberList(self) -> [_DAVResource]:
|
646
|
721
|
members = []
|
647
|
722
|
|
648
|
|
- parent_id = None if self.content is None else self.content.id
|
|
723
|
+ if self.content:
|
|
724
|
+ children = self.content.children
|
|
725
|
+ else:
|
|
726
|
+ children = self.content_api.get_all(None, ContentType.Any, self.workspace)
|
649
|
727
|
|
650
|
|
- for content in self.content_api.get_all_with_filter(parent_id, ContentType.Any, self.workspace):
|
|
728
|
+ for content in children:
|
651
|
729
|
content_path = '%s/%s' % (self.path, self.provider.transform_to_display(content.get_label()))
|
652
|
730
|
|
653
|
731
|
if content.type == ContentType.Folder:
|
654
|
|
- members.append(Folder(content_path, self.environ, content, self.workspace))
|
|
732
|
+ members.append(Folder(content_path, self.environ, self.workspace, content))
|
655
|
733
|
elif content.type == ContentType.File:
|
656
|
734
|
self._file_count += 1
|
657
|
735
|
members.append(File(content_path, self.environ, content))
|
|
@@ -674,8 +752,12 @@ class ArchivedFolder(HistoryFolder):
|
674
|
752
|
|
675
|
753
|
|
676
|
754
|
class HistoryFileFolder(HistoryFolder):
|
|
755
|
+ """
|
|
756
|
+ A virtual resource that contains for a given content (file/page/thread) all its revisions
|
|
757
|
+ """
|
|
758
|
+
|
677
|
759
|
def __init__(self, path: str, environ: dict, content: data.Content):
|
678
|
|
- super(HistoryFileFolder, self).__init__(path, environ, content.workspace, HistoryType.All, content)
|
|
760
|
+ super(HistoryFileFolder, self).__init__(path, environ, content.workspace, content, HistoryType.All)
|
679
|
761
|
|
680
|
762
|
def __repr__(self) -> str:
|
681
|
763
|
return "<DAVCollection: HistoryFileFolder (%s)" % self.content.file_name
|
|
@@ -686,16 +768,11 @@ class HistoryFileFolder(HistoryFolder):
|
686
|
768
|
def createCollection(self, name):
|
687
|
769
|
raise DAVError(HTTP_FORBIDDEN)
|
688
|
770
|
|
689
|
|
- def createEmptyResource(self, name) ->FakeFileStream:
|
690
|
|
- return FakeFileStream(
|
691
|
|
- content=self.content,
|
692
|
|
- content_api=self.content_api,
|
693
|
|
- file_name=name,
|
694
|
|
- workspace=self.content.workspace
|
695
|
|
- )
|
696
|
|
-
|
697
|
771
|
def getMemberNames(self) -> [int]:
|
698
|
|
- """ Usually we would return a string, but here as it can be the same name because that's history, we get the id"""
|
|
772
|
+ """
|
|
773
|
+ Usually we would return a string, but here as we're working with different
|
|
774
|
+ revisions of the same content, we'll work with revision_id
|
|
775
|
+ """
|
699
|
776
|
ret = []
|
700
|
777
|
|
701
|
778
|
for content in self.content.revisions:
|
|
@@ -722,9 +799,6 @@ class HistoryFileFolder(HistoryFolder):
|
722
|
799
|
content=self.content,
|
723
|
800
|
content_revision=revision)
|
724
|
801
|
|
725
|
|
- def delete(self):
|
726
|
|
- raise DAVError(HTTP_FORBIDDEN)
|
727
|
|
-
|
728
|
802
|
def getMemberList(self) -> [_DAVResource]:
|
729
|
803
|
members = []
|
730
|
804
|
|
|
@@ -751,6 +825,9 @@ class HistoryFileFolder(HistoryFolder):
|
751
|
825
|
|
752
|
826
|
|
753
|
827
|
class File(DAVNonCollection):
|
|
828
|
+ """
|
|
829
|
+ File resource corresponding to tracim's files
|
|
830
|
+ """
|
754
|
831
|
def __init__(self, path: str, environ: dict, content: Content):
|
755
|
832
|
super(File, self).__init__(path, environ)
|
756
|
833
|
|
|
@@ -758,6 +835,9 @@ class File(DAVNonCollection):
|
758
|
835
|
self.user = UserApi(None).get_one_by_email(environ['http_authenticator.username'])
|
759
|
836
|
self.content_api = ContentApi(self.user)
|
760
|
837
|
|
|
838
|
+ # this is the property that windows client except to check if the file is read-write or read-only,
|
|
839
|
+ # but i wasn't able to set this property so you'll have to look into it >.>
|
|
840
|
+ # self.setPropertyValue('Win32FileAttributes', '00000021')
|
761
|
841
|
|
762
|
842
|
def getPreferredPath(self):
|
763
|
843
|
fix_txt = '.txt' if self.getContentType() == 'text/plain' else mimetypes.guess_extension(self.getContentType())
|
|
@@ -797,7 +877,8 @@ class File(DAVNonCollection):
|
797
|
877
|
content=self.content,
|
798
|
878
|
content_api=self.content_api,
|
799
|
879
|
file_name=self.content.get_label(),
|
800
|
|
- workspace=self.content.workspace
|
|
880
|
+ workspace=self.content.workspace,
|
|
881
|
+ path=self.path
|
801
|
882
|
)
|
802
|
883
|
|
803
|
884
|
def moveRecursive(self, destpath):
|
|
@@ -816,7 +897,7 @@ class File(DAVNonCollection):
|
816
|
897
|
current_path = re.sub(r'/\.(deleted|archived)', '', self.path)
|
817
|
898
|
|
818
|
899
|
if current_path == destpath:
|
819
|
|
- Manage(
|
|
900
|
+ ManageActions(
|
820
|
901
|
ActionDescription.UNDELETION if self.content.is_deleted else ActionDescription.UNARCHIVING,
|
821
|
902
|
self.content_api,
|
822
|
903
|
self.content
|
|
@@ -830,7 +911,7 @@ class File(DAVNonCollection):
|
830
|
911
|
dest_path = re.sub(r'/\.(deleted|archived)', '', destpath)
|
831
|
912
|
|
832
|
913
|
if dest_path == self.path:
|
833
|
|
- Manage(
|
|
914
|
+ ManageActions(
|
834
|
915
|
ActionDescription.DELETION if '.deleted' in destpath else ActionDescription.ARCHIVING,
|
835
|
916
|
self.content_api,
|
836
|
917
|
self.content
|
|
@@ -880,10 +961,13 @@ class File(DAVNonCollection):
|
880
|
961
|
return True
|
881
|
962
|
|
882
|
963
|
def delete(self):
|
883
|
|
- Manage(ActionDescription.DELETION, self.content_api, self.content).action()
|
|
964
|
+ ManageActions(ActionDescription.DELETION, self.content_api, self.content).action()
|
884
|
965
|
|
885
|
966
|
|
886
|
967
|
class HistoryFile(File):
|
|
968
|
+ """
|
|
969
|
+ A virtual resource corresponding to a specific tracim's revision's file
|
|
970
|
+ """
|
887
|
971
|
def __init__(self, path: str, environ: dict, content: data.Content, content_revision: data.ContentRevisionRO):
|
888
|
972
|
super(HistoryFile, self).__init__(path, environ, content)
|
889
|
973
|
self.content_revision = content_revision
|
|
@@ -914,20 +998,14 @@ class HistoryFile(File):
|
914
|
998
|
def delete(self):
|
915
|
999
|
raise DAVError(HTTP_FORBIDDEN)
|
916
|
1000
|
|
917
|
|
- def handleDelete(self):
|
918
|
|
- return False
|
919
|
|
-
|
920
|
|
- def handleCopy(self, destpath, depth_infinity):
|
921
|
|
- return True
|
922
|
|
-
|
923
|
|
- def handleMove(self, destpath):
|
924
|
|
- return True
|
925
|
|
-
|
926
|
1001
|
def copyMoveSingle(self, destpath, ismove):
|
927
|
1002
|
raise DAVError(HTTP_FORBIDDEN)
|
928
|
1003
|
|
929
|
1004
|
|
930
|
1005
|
class OtherFile(File):
|
|
1006
|
+ """
|
|
1007
|
+ File resource corresponding to tracim's page and thread
|
|
1008
|
+ """
|
931
|
1009
|
def __init__(self, path: str, environ: dict, content: data.Content):
|
932
|
1010
|
super(OtherFile, self).__init__(path, environ, content)
|
933
|
1011
|
|
|
@@ -935,7 +1013,7 @@ class OtherFile(File):
|
935
|
1013
|
|
936
|
1014
|
self.content_designed = self.design()
|
937
|
1015
|
|
938
|
|
- # workaroung for consistent request as we have to return a resource with a path ending with .html
|
|
1016
|
+ # workaround for consistent request as we have to return a resource with a path ending with .html
|
939
|
1017
|
# when entering folder for windows, but only once because when we select it again it would have .html.html
|
940
|
1018
|
# which is no good
|
941
|
1019
|
if not self.path.endswith('.html'):
|
|
@@ -975,6 +1053,9 @@ class OtherFile(File):
|
975
|
1053
|
|
976
|
1054
|
|
977
|
1055
|
class HistoryOtherFile(OtherFile):
|
|
1056
|
+ """
|
|
1057
|
+ A virtual resource corresponding to a specific tracim's revision's page and thread
|
|
1058
|
+ """
|
978
|
1059
|
def __init__(self, path: str, environ: dict, content: data.Content, content_revision: data.ContentRevisionRO):
|
979
|
1060
|
super(HistoryOtherFile, self).__init__(path, environ, content)
|
980
|
1061
|
self.content_revision = content_revision
|
|
@@ -995,21 +1076,8 @@ class HistoryOtherFile(OtherFile):
|
995
|
1076
|
|
996
|
1077
|
return filestream
|
997
|
1078
|
|
998
|
|
- def beginWrite(self, contentType=None):
|
999
|
|
- raise DAVError(HTTP_FORBIDDEN)
|
1000
|
|
-
|
1001
|
1079
|
def delete(self):
|
1002
|
1080
|
raise DAVError(HTTP_FORBIDDEN)
|
1003
|
1081
|
|
1004
|
|
- def handleDelete(self):
|
1005
|
|
- return True
|
1006
|
|
-
|
1007
|
|
- def handleCopy(self, destpath, depth_infinity):
|
1008
|
|
- return True
|
1009
|
|
-
|
1010
|
|
- def handleMove(self, destpath):
|
1011
|
|
- return True
|
1012
|
|
-
|
1013
|
1082
|
def copyMoveSingle(self, destpath, ismove):
|
1014
|
1083
|
raise DAVError(HTTP_FORBIDDEN)
|
1015
|
|
-
|