浏览代码

New migration for temporary files, updating content's model and webdav to fit them, reducing number of calls to database with request

Nonolost 8 年前
父节点
当前提交
bb447d7988

+ 8 - 2
tracim/migration/versions/b4b8d57b54e5_add_hash_column_for_digest_.py 查看文件

11
 down_revision = '534c4594ed29'
11
 down_revision = '534c4594ed29'
12
 
12
 
13
 from alembic import op
13
 from alembic import op
14
-from sqlalchemy import Column, Unicode
14
+from sqlalchemy import Column, Unicode, Boolean
15
 
15
 
16
 
16
 
17
 def upgrade():
17
 def upgrade():
18
     op.add_column('users', Column('webdav_left_digest_response_hash', Unicode(128)))
18
     op.add_column('users', Column('webdav_left_digest_response_hash', Unicode(128)))
19
-
19
+    op.add_column('content_revisions', Column('is_temporary', Boolean(), unique=False, nullable=True))
20
+    op.execute('''
21
+        UPDATE content_revisions
22
+        SET is_temporary = FALSE
23
+        ''')
24
+    op.alter_column('content_revisions', 'is_temporary', nullable=False)
20
 
25
 
21
 def downgrade():
26
 def downgrade():
22
     op.drop_column('users', 'webdav_left_digest_response_hash')
27
     op.drop_column('users', 'webdav_left_digest_response_hash')
28
+    op.drop_column('content_revisions', 'is_temporary')

+ 35 - 1
tracim/tracim/lib/content.py 查看文件

80
             current_user: User,
80
             current_user: User,
81
             show_archived=False,
81
             show_archived=False,
82
             show_deleted=False,
82
             show_deleted=False,
83
+            show_temporary=False,
83
             all_content_in_treeview=True,
84
             all_content_in_treeview=True,
84
             force_show_all_types=False,
85
             force_show_all_types=False,
85
             disable_user_workspaces_filter=False,
86
             disable_user_workspaces_filter=False,
88
         self._user_id = current_user.user_id if current_user else None
89
         self._user_id = current_user.user_id if current_user else None
89
         self._show_archived = show_archived
90
         self._show_archived = show_archived
90
         self._show_deleted = show_deleted
91
         self._show_deleted = show_deleted
92
+        self._show_temporary = show_temporary
91
         self._show_all_type_of_contents_in_treeview = all_content_in_treeview
93
         self._show_all_type_of_contents_in_treeview = all_content_in_treeview
92
         self._force_show_all_types = force_show_all_types
94
         self._force_show_all_types = force_show_all_types
93
         self._disable_user_workspaces_filter = disable_user_workspaces_filter
95
         self._disable_user_workspaces_filter = disable_user_workspaces_filter
200
         if not self._show_archived:
202
         if not self._show_archived:
201
             result = result.filter(Content.is_archived==False)
203
             result = result.filter(Content.is_archived==False)
202
 
204
 
205
+        if not self._show_temporary:
206
+            result = result.filter(Content.is_temporary==False)
207
+
203
         return result
208
         return result
204
 
209
 
205
     def __revisions_real_base_query(self, workspace: Workspace=None):
210
     def __revisions_real_base_query(self, workspace: Workspace=None):
230
         if not self._show_archived:
235
         if not self._show_archived:
231
             result = result.filter(ContentRevisionRO.is_archived==False)
236
             result = result.filter(ContentRevisionRO.is_archived==False)
232
 
237
 
238
+        if not self._show_temporary:
239
+            result = result.filter(Content.is_temporary==False)
240
+
233
         return result
241
         return result
234
 
242
 
235
     def _hard_filtered_base_query(self, workspace: Workspace=None):
243
     def _hard_filtered_base_query(self, workspace: Workspace=None):
256
                 filter(Content.is_archived==False).\
264
                 filter(Content.is_archived==False).\
257
                 filter(parent.is_archived==False)
265
                 filter(parent.is_archived==False)
258
 
266
 
267
+        if not self._show_temporary:
268
+            parent = aliased(Content)
269
+            result = result.join(parent, Content.parent). \
270
+                filter(Content.is_temporary == False). \
271
+                filter(parent.is_temporary == False)
272
+
259
         return result
273
         return result
260
 
274
 
261
     def get_child_folders(self, parent: Content=None, workspace: Workspace=None, filter_by_allowed_content_types: list=[], removed_item_ids: list=[], allowed_node_types=None) -> [Content]:
275
     def get_child_folders(self, parent: Content=None, workspace: Workspace=None, filter_by_allowed_content_types: list=[], removed_item_ids: list=[], allowed_node_types=None) -> [Content]:
302
 
316
 
303
         return result
317
         return result
304
 
318
 
305
-    def create(self, content_type: str, workspace: Workspace, parent: Content=None, label:str ='', do_save=False) -> Content:
319
+    def create(self, content_type: str, workspace: Workspace, parent: Content=None, label:str ='', is_temporary:bool =False, do_save=False) -> Content:
306
         assert content_type in ContentType.allowed_types()
320
         assert content_type in ContentType.allowed_types()
307
         content = Content()
321
         content = Content()
308
         content.owner = self._user
322
         content.owner = self._user
310
         content.workspace = workspace
324
         content.workspace = workspace
311
         content.type = content_type
325
         content.type = content_type
312
         content.label = label
326
         content.label = label
327
+        content.is_temporary = is_temporary
313
         content.revision_type = ActionDescription.CREATION
328
         content.revision_type = ActionDescription.CREATION
314
 
329
 
315
         if do_save:
330
         if do_save:
416
 
431
 
417
         return resultset.all()
432
         return resultset.all()
418
 
433
 
434
+    # TODO find an other name to filter on is_deleted / is_archived
435
+    def get_all_with_filter(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> [Content]:
436
+        assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
437
+        assert content_type is not None# DYN_REMOVE
438
+        assert isinstance(content_type, str) # DYN_REMOVE
439
+
440
+        resultset = self._base_query(workspace)
441
+
442
+        if content_type != ContentType.Any:
443
+            resultset = resultset.filter(Content.type==content_type)
444
+
445
+        resultset = resultset.filter(Content.is_deleted == self._show_deleted)
446
+        resultset = resultset.filter(Content.is_archived == self._show_archived)
447
+        resultset = resultset.filter(Content.is_temporary == self._show_temporary)
448
+
449
+        resultset = resultset.filter(Content.parent_id==parent_id)
450
+
451
+        return resultset.all()
452
+
419
     def get_all_without_exception(self, content_type: str, workspace: Workspace=None) -> [Content]:
453
     def get_all_without_exception(self, content_type: str, workspace: Workspace=None) -> [Content]:
420
         assert content_type is not None# DYN_REMOVE
454
         assert content_type is not None# DYN_REMOVE
421
 
455
 

+ 5 - 2
tracim/tracim/lib/webdav/__init__.py 查看文件

60
             self.update_file(self._buff)
60
             self.update_file(self._buff)
61
 
61
 
62
     def create_file(self, item_content):
62
     def create_file(self, item_content):
63
+        is_temporary = self._file_name.startswith('.~') or self._file_name.startswith('~')
64
+
63
         file = self._api.create(
65
         file = self._api.create(
64
             content_type=ContentType.File,
66
             content_type=ContentType.File,
65
             workspace=self._workspace,
67
             workspace=self._workspace,
66
-            parent=self._parent
67
-            )
68
+            parent=self._parent,
69
+            is_temporary=is_temporary
70
+        )
68
 
71
 
69
         self._api.update_file_data(
72
         self._api.update_file_data(
70
             file,
73
             file,

+ 26 - 17
tracim/tracim/lib/webdav/design.py 查看文件

66
                        date,
66
                        date,
67
                        event.owner.display_name,
67
                        event.owner.display_name,
68
                        '<i class="fa fa-caret-left"></i> shown' if event.id == content_revision.revision_id else '''<span><a class="revision-link" href="/.history/%s/%s-%s">(View revision)</a></span>''' % (
68
                        '<i class="fa fa-caret-left"></i> shown' if event.id == content_revision.revision_id else '''<span><a class="revision-link" href="/.history/%s/%s-%s">(View revision)</a></span>''' % (
69
-                       content_revision.label, event.id, event.ref_object.label) if event.type.id in ['revision', 'creation', 'edition'] else '')
69
+                       content.label, event.id, event.ref_object.label) if event.type.id in ['revision', 'creation', 'edition'] else '')
70
 
70
 
71
     histHTML += '</table>'
71
     histHTML += '</table>'
72
 
72
 
78
 	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
78
 	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
79
 	<link rel="stylesheet" href="/home/arnaud/Documents/css/style.css">
79
 	<link rel="stylesheet" href="/home/arnaud/Documents/css/style.css">
80
 	<script type="text/javascript" src="/home/arnaud/Documents/css/script.js"></script>
80
 	<script type="text/javascript" src="/home/arnaud/Documents/css/script.js"></script>
81
+	<script
82
+			  src="https://code.jquery.com/jquery-3.1.0.min.js"
83
+			  integrity="sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s="
84
+			  crossorigin="anonymous"></script>
81
 </head>
85
 </head>
82
 <body>
86
 <body>
83
     <div id="left" class="col-lg-8 col-md-12 col-sm-12 col-xs-12">
87
     <div id="left" class="col-lg-8 col-md-12 col-sm-12 col-xs-12">
105
     </div>
109
     </div>
106
     <script type="text/javascript">
110
     <script type="text/javascript">
107
         window.onload = function() {
111
         window.onload = function() {
108
-            elems = document.getElementsByClassName('revision-link');
109
-            for(var i = 0; i<elems.length; i++) {
110
-                test = window.location.href
111
-                test += "/.." + elems[i].href.replace(/file:\/\//, "")
112
-                elems[i].href = test
113
-            }
112
+            file_location = window.location.href
113
+            file_location = file_location.replace(/\/[^/]*$/, '')
114
+            file_location = file_location.replace(/\/.history\/[^/]*$/, '')
115
+
116
+            $('.revision-link').each(function() {
117
+                $(this).attr('href', file_location + $(this).attr('href'))
118
+            });
114
         }
119
         }
115
     </script>
120
     </script>
116
 </body>
121
 </body>
173
                     label = _LABELS[t.type.id]
178
                     label = _LABELS[t.type.id]
174
 
179
 
175
                     disc += '''
180
                     disc += '''
176
-                    <div class="row comment comment-row to-hide">
181
+                    <div class="%s row comment comment-row to-hide">
177
                         <i class="fa %s comment-icon"></i>
182
                         <i class="fa %s comment-icon"></i>
178
                             <div class="comment-content">
183
                             <div class="comment-content">
179
                             <h5>
184
                             <h5>
183
                             %s %s
188
                             %s %s
184
                         </div>
189
                         </div>
185
                     </div>
190
                     </div>
186
-                    ''' % (t.type.icon,
191
+                    ''' % ('warning' if t.id == content_revision.revision_id else '',
192
+                           t.type.icon,
187
                            t.owner.display_name,
193
                            t.owner.display_name,
188
                            t.create_readable_date(),
194
                            t.create_readable_date(),
189
                            label,
195
                            label,
190
-                           '''<span><a class="revision-link" href="/.history/%s/%s-%s">(View revision)</a></span>''' % (
191
-                               content_revision.label,
196
+                            '<i class="fa fa-caret-left"></i> shown' if t.id == content_revision.revision_id else '''<span><a class="revision-link" href="/.history/%s/%s-%s">(View revision)</a></span>''' % (
197
+                               content.label,
192
                                t.id,
198
                                t.id,
193
                                t.ref_object.label) if t.type.id in ['revision', 'creation', 'edition'] else '')
199
                                t.ref_object.label) if t.type.id in ['revision', 'creation', 'edition'] else '')
194
 
200
 
230
     </div>
236
     </div>
231
     <script type="text/javascript">
237
     <script type="text/javascript">
232
         window.onload = function() {
238
         window.onload = function() {
233
-            elems = document.getElementsByClassName('revision-link');
234
-            for(var i = 0; i<elems.length; i++) {
235
-                test = window.location.href
236
-                test += "/.." + elems[i].href.replace(/file:\/\//, "")
237
-                elems[i].href = test
238
-            }
239
+            file_location = window.location.href
240
+            file_location = file_location.replace(/\/[^/]*$/, '')
241
+            file_location = file_location.replace(/\/.history\/[^/]*$/, '')
242
+
243
+            $('.revision-link').each(function() {
244
+                $(this).attr('href', file_location + $(this).attr('href'))
245
+            });
239
         }
246
         }
240
 
247
 
241
         function hide_elements() {
248
         function hide_elements() {
246
                     $(elems[i]).hide();
253
                     $(elems[i]).hide();
247
                 }
254
                 }
248
                 while (elems.length>0) {
255
                 while (elems.length>0) {
256
+                    $(elems[0]).removeClass('comment-row');
249
                     $(elems[0]).removeClass('to-hide');
257
                     $(elems[0]).removeClass('to-hide');
250
                 }
258
                 }
251
                 $('#hideshow').addClass('fa-eye').removeClass('fa-eye-slash');
259
                 $('#hideshow').addClass('fa-eye').removeClass('fa-eye-slash');
254
             else {
262
             else {
255
                 elems = document.getElementsByClassName('to-show');
263
                 elems = document.getElementsByClassName('to-show');
256
                 for(var i = 0; i<elems.length; i++) {
264
                 for(var i = 0; i<elems.length; i++) {
265
+                    $(elems[0]).addClass('comment-row');
257
                     $(elems[i]).addClass('to-hide');
266
                     $(elems[i]).addClass('to-hide');
258
                     $(elems[i]).show();
267
                     $(elems[i]).show();
259
                 }
268
                 }

+ 18 - 25
tracim/tracim/lib/webdav/sql_dav_provider.py 查看文件

61
     #########################################################
61
     #########################################################
62
     # Everything override from DAVProvider
62
     # Everything override from DAVProvider
63
     def getResourceInst(self, path, environ):
63
     def getResourceInst(self, path, environ):
64
-        print("ok : ", path)
65
         #if not self.exists(path, environ):
64
         #if not self.exists(path, environ):
66
         #    return None
65
         #    return None
67
         if not self.exists(path, environ):
66
         if not self.exists(path, environ):
68
             return None
67
             return None
69
 
68
 
70
-        uapi = UserApi(None)
71
-        environ['user'] = uapi.get_one_by_email(environ['http_authenticator.username'])
72
-
73
         norm_path = normpath(path)
69
         norm_path = normpath(path)
74
-        norm_path = self.transform_to_display(norm_path)
75
 
70
 
76
         root_path = environ['http_authenticator.realm']
71
         root_path = environ['http_authenticator.realm']
77
         parent_path = dirname(norm_path)
72
         parent_path = dirname(norm_path)
78
 
73
 
79
-        workspace_api = WorkspaceApi(environ['user'])
74
+        user = UserApi(None).get_one_by_email(environ['http_authenticator.username'])
75
+
76
+        workspace_api = WorkspaceApi(user)
80
         content_api = ContentApi(
77
         content_api = ContentApi(
81
-            environ['user'],
78
+            user,
82
             show_archived=self._show_archive,
79
             show_archived=self._show_archive,
83
             show_deleted=self._show_delete
80
             show_deleted=self._show_delete
84
         )
81
         )
155
             )
152
             )
156
 
153
 
157
         # is history
154
         # is history
158
-        is_history_file = re.search(r'/\.history/[^/]+/(\d+)-.+', norm_path) is not None
155
+        is_history_file = re.search(r'/\.history/[^/]+/\((\d+) - [a-zA-Z]+\) .+', norm_path) is not None
159
 
156
 
160
         if self._show_history and is_history_file:
157
         if self._show_history and is_history_file:
161
-            content_revision = content_api.get_one_revision(re.search(r'/\.history/[^/]+/(\d+)-.+', norm_path).group(1))
158
+            content_revision = content_api.get_one_revision(re.search(r'/\.history/[^/]+/\((\d+) - [a-zA-Z]+\) .+', norm_path).group(1))
162
             content = self.get_content_from_revision(content_revision, content_api)
159
             content = self.get_content_from_revision(content_revision, content_api)
163
 
160
 
164
             if content.type == ContentType.File:
161
             if content.type == ContentType.File:
165
-                return sql_resources.HistoryFile(path, environ, content, content_revision)
162
+                return sql_resources.HistoryFile(norm_path, environ, content, content_revision)
166
             else:
163
             else:
167
-                return sql_resources.HistoryOtherFile(path, environ, content, content_revision)
164
+                return sql_resources.HistoryOtherFile(norm_path, environ, content, content_revision)
168
 
165
 
169
         # other
166
         # other
170
         if content is None:
167
         if content is None:
171
             return None
168
             return None
172
         if content.type == ContentType.Folder:
169
         if content.type == ContentType.Folder:
173
-            return sql_resources.Folder(path, environ, content, content.workspace)
170
+            return sql_resources.Folder(norm_path, environ, content, content.workspace)
174
         elif content.type == ContentType.File:
171
         elif content.type == ContentType.File:
175
-            return sql_resources.File(path, environ, content)
172
+            return sql_resources.File(norm_path, environ, content)
176
         elif content.type in [ContentType.Page, ContentType.Thread]:
173
         elif content.type in [ContentType.Page, ContentType.Thread]:
177
-            return sql_resources.OtherFile(path, environ, content)
174
+            return sql_resources.OtherFile(norm_path, environ, content)
178
         else:
175
         else:
179
             return None
176
             return None
180
 
177
 
181
     def exists(self, path, environ):
178
     def exists(self, path, environ):
182
-        print("ok (exist) : ", path)
183
-        uapi = UserApi(None)
184
-        environ['user'] = uapi.get_one_by_email(environ['http_authenticator.username'])
185
-
186
         norm_path = normpath(path)
179
         norm_path = normpath(path)
187
         parent_path = dirname(norm_path)
180
         parent_path = dirname(norm_path)
188
         root_path = environ['http_authenticator.realm']
181
         root_path = environ['http_authenticator.realm']
189
 
182
 
190
-        workspace_api = WorkspaceApi(environ['user'])
183
+        user = UserApi(None).get_one_by_email(environ['http_authenticator.username'])
184
+        workspace_api = WorkspaceApi(user)
191
         content_api = ContentApi(
185
         content_api = ContentApi(
192
-            current_user=environ['user'],
186
+            current_user=user,
193
             show_archived=True,
187
             show_archived=True,
194
             show_deleted=True
188
             show_deleted=True
195
         )
189
         )
196
 
190
 
197
         if path == root_path:
191
         if path == root_path:
198
-            print("ok (pass ici) : ", path)
199
             return True
192
             return True
200
         elif parent_path == root_path:
193
         elif parent_path == root_path:
201
             return self.get_workspace_from_path(
194
             return self.get_workspace_from_path(
207
 
200
 
208
         is_deleted = re.search(r'/\.deleted/(\.history/)?(?!\.history)[^/]*(/\.)?(history|deleted|archived)?$', norm_path) is not None
201
         is_deleted = re.search(r'/\.deleted/(\.history/)?(?!\.history)[^/]*(/\.)?(history|deleted|archived)?$', norm_path) is not None
209
 
202
 
210
-        revision_id = re.search(r'/\.history/[^/]+/(\d+)-([^/].+)$', norm_path)
203
+        revision_id = re.search(r'/\.history/[^/]+/\((\d+) - [a-zA-Z]+\) ([^/].+)$', norm_path)
211
 
204
 
212
         blbl = self.reduce_path(norm_path)
205
         blbl = self.reduce_path(norm_path)
213
         if dirname(blbl) == '/':
206
         if dirname(blbl) == '/':
238
         workspace = self.get_workspace_from_path(path, workspace_api)
231
         workspace = self.get_workspace_from_path(path, workspace_api)
239
 
232
 
240
         try:
233
         try:
241
-            if basename(dirname(path)) == workspace.label:
234
+            if dirname(dirname(path)) == '/':
242
                 return content_api.get_one_by_label_and_parent(
235
                 return content_api.get_one_by_label_and_parent(
243
                     self.transform_to_bdd(basename(path)),
236
                     self.transform_to_bdd(basename(path)),
244
                     workspace=workspace
237
                     workspace=workspace
271
 
264
 
272
     def transform_to_display(self, string):
265
     def transform_to_display(self, string):
273
         _TO_DISPLAY = {
266
         _TO_DISPLAY = {
274
-            # '/':'⁄',
267
+            '/':'⧸',
275
             '\\': '⧹',
268
             '\\': '⧹',
276
             ':': '∶',
269
             ':': '∶',
277
             '*': '∗',
270
             '*': '∗',
289
 
282
 
290
     def transform_to_bdd(self, string):
283
     def transform_to_bdd(self, string):
291
         _TO_BDD = {
284
         _TO_BDD = {
292
-            # '⁄': '/',
285
+            '⧸': '/',
293
             '⧹': '\\',
286
             '⧹': '\\',
294
             '∶': ':',
287
             '∶': ':',
295
             '∗': '*',
288
             '∗': '*',

+ 3 - 0
tracim/tracim/lib/webdav/sql_domain_controller.py 查看文件

37
     def get_left_digest_response_hash(self, realmname, username, environ):
37
     def get_left_digest_response_hash(self, realmname, username, environ):
38
         try:
38
         try:
39
             user = self._api.get_one_by_email(username)
39
             user = self._api.get_one_by_email(username)
40
+            environ['user_api'] = UserApi(user)
41
+            print("hey ! ", realmname)
40
             return user.webdav_left_digest_response_hash
42
             return user.webdav_left_digest_response_hash
41
         except:
43
         except:
42
             return None
44
             return None
43
 
45
 
44
     def authDomainUser(self, realmname, username, password, environ):
46
     def authDomainUser(self, realmname, username, password, environ):
45
         '''Vérifier que l'utilisateur est valide pour ce domaine'''
47
         '''Vérifier que l'utilisateur est valide pour ce domaine'''
48
+
46
         return self.isRealmUser(realmname, username, environ) and \
49
         return self.isRealmUser(realmname, username, environ) and \
47
             self._api.get_one_by_email(username).validate_password(password)
50
             self._api.get_one_by_email(username).validate_password(password)

+ 199 - 156
tracim/tracim/lib/webdav/sql_resources.py 查看文件

7
 import mimetypes
7
 import mimetypes
8
 
8
 
9
 from tracim.lib.content import ContentApi
9
 from tracim.lib.content import ContentApi
10
+from tracim.lib.user import UserApi
10
 from tracim.lib.webdav import HistoryType
11
 from tracim.lib.webdav import HistoryType
11
 from tracim.lib.webdav import FakeFileStream
12
 from tracim.lib.webdav import FakeFileStream
12
 from tracim.lib.workspace import WorkspaceApi
13
 from tracim.lib.workspace import WorkspaceApi
14
 from tracim.model.data import Content, ActionDescription
15
 from tracim.model.data import Content, ActionDescription
15
 from tracim.model.data import ContentType
16
 from tracim.model.data import ContentType
16
 from tracim.lib.webdav.design import designThread, designPage
17
 from tracim.lib.webdav.design import designThread, designPage
17
-from tracim.lib.user import UserApi
18
 
18
 
19
 from wsgidav import compat
19
 from wsgidav import compat
20
 from wsgidav.dav_error import DAVError, HTTP_FORBIDDEN
20
 from wsgidav.dav_error import DAVError, HTTP_FORBIDDEN
21
 from wsgidav.dav_provider import DAVCollection, DAVNonCollection
21
 from wsgidav.dav_provider import DAVCollection, DAVNonCollection
22
 from wsgidav.dav_provider import _DAVResource
22
 from wsgidav.dav_provider import _DAVResource
23
 
23
 
24
+from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
24
 
25
 
25
 class Manage(object):
26
 class Manage(object):
26
     """Objet qui sert à encapsuler l'exécution des actions de l'api archive/delete..."""
27
     """Objet qui sert à encapsuler l'exécution des actions de l'api archive/delete..."""
44
         self._new_name = self.make_name()
45
         self._new_name = self.make_name()
45
 
46
 
46
     def action(self):
47
     def action(self):
47
-        """Exécute l'action"""
48
-        with new_revision(self.content):
49
-            self._actions[self._type](self.content)
50
-
51
-            if self.content.label == '':
52
-                self.content.file_name = self._new_name
53
-            else:
54
-                self.content.label = self._new_name
55
-
56
-            self.content_api.save(self.content, self._type)
48
+        try:
49
+            self.content_api.get_one_by_label_and_parent(self._new_name, self.content.parent, self.content.workspace)
50
+            raise DAVError(HTTP_FORBIDDEN)
51
+        except NoResultFound:
52
+            """Exécute l'action"""
53
+            with new_revision(self.content):
54
+                self.content_api.update_content(self.content, self._new_name)
55
+                self._actions[self._type](self.content)
56
+                self.content_api.save(self.content, self._type)
57
 
57
 
58
-        transaction.commit()
58
+            transaction.commit()
59
 
59
 
60
     def make_name(self):
60
     def make_name(self):
61
         """Créer le nouveau nom : rajoute de - archive date / retrait de - archive date suivant l'action"""
61
         """Créer le nouveau nom : rajoute de - archive date / retrait de - archive date suivant l'action"""
63
         extension = ''
63
         extension = ''
64
         is_file_name = self.content.label == ''
64
         is_file_name = self.content.label == ''
65
 
65
 
66
+
66
         if is_file_name:
67
         if is_file_name:
67
-            extension = re.search(r'(\.[^.]*)', new_name).group(0)
68
-            new_name = re.sub(r'(\.[^.]*)', '', new_name)
68
+            extension = re.search(r'(\.[^.]+)$', new_name).group(0)
69
+            new_name = re.sub(r'(\.[^.]+)$', '', new_name)
69
 
70
 
70
         if self._type in [ActionDescription.ARCHIVING, ActionDescription.DELETION]:
71
         if self._type in [ActionDescription.ARCHIVING, ActionDescription.DELETION]:
71
-            new_name += ' - %s the %s' % (self._to_name[self._type], datetime.now())
72
+            new_name += ' - %s the %s' % (self._to_name[self._type], datetime.now().strftime('%d-%m-%Y at %H:%M'))
72
         else:
73
         else:
73
             new_name = re.sub(r'( - (%s|%s) the .*)$' % (self._to_name[ActionDescription.DELETION], self._to_name[ActionDescription.ARCHIVING]), '', new_name)
74
             new_name = re.sub(r'( - (%s|%s) the .*)$' % (self._to_name[ActionDescription.DELETION], self._to_name[ActionDescription.ARCHIVING]), '', new_name)
74
 
75
 
83
     def __init__(self, path: str, environ: dict):
84
     def __init__(self, path: str, environ: dict):
84
         super(Root, self).__init__(path, environ)
85
         super(Root, self).__init__(path, environ)
85
 
86
 
86
-        self.workspace_api = WorkspaceApi(environ['user'])
87
+        self.user = UserApi(None).get_one_by_email(environ['http_authenticator.username'])
88
+        self.workspace_api = WorkspaceApi(self.user)
87
 
89
 
88
     def __repr__(self) -> str:
90
     def __repr__(self) -> str:
89
         return '<DAVCollection: Root>'
91
         return '<DAVCollection: Root>'
100
         """
102
         """
101
         try:
103
         try:
102
             workspace = self.workspace_api.get_one_by_label(label)
104
             workspace = self.workspace_api.get_one_by_label(label)
103
-            workspace_path = '%s%s%s' % (self.path, '' if self.path == '/' else '/', workspace.label)
105
+            workspace_path = '%s%s%s' % (self.path, '' if self.path == '/' else '/', self.provider.transform_to_display(workspace.label))
104
 
106
 
105
             return Workspace(workspace_path, self.environ, workspace)
107
             return Workspace(workspace_path, self.environ, workspace)
106
         except AttributeError:
108
         except AttributeError:
122
         [For now] we don't allow to create new workspaces through
124
         [For now] we don't allow to create new workspaces through
123
         webdav client. Though if we come to allow it, deleting the error's raise will
125
         webdav client. Though if we come to allow it, deleting the error's raise will
124
         make it possible."""
126
         make it possible."""
125
-        # TODO : remove comment here] raise DAVError(HTTP_FORBIDDEN)
127
+        # TODO : remove comment here
128
+        # raise DAVError(HTTP_FORBIDDEN)
126
 
129
 
127
         new_workspace = self.workspace_api.create_workspace(name)
130
         new_workspace = self.workspace_api.create_workspace(name)
128
         self.workspace_api.save(new_workspace)
131
         self.workspace_api.save(new_workspace)
132
+
133
+        workspace_path = '%s%s%s' % (
134
+        self.path, '' if self.path == '/' else '/', self.provider.transform_to_display(new_workspace.label))
135
+
129
         transaction.commit()
136
         transaction.commit()
137
+        return Workspace(workspace_path, self.environ, new_workspace)
138
+
139
+    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à !
142
+
143
+        members = []
144
+        for workspace in self.workspace_api.get_all():
145
+            workspace_path = '%s%s%s' % (self.path, '' if self.path == '/' else '/', workspace.label)
146
+            members.append(Workspace(workspace_path, self.environ, workspace))
147
+
148
+        return members
130
 
149
 
131
 
150
 
132
 class Workspace(DAVCollection):
151
 class Workspace(DAVCollection):
138
 
157
 
139
         self.workspace = workspace
158
         self.workspace = workspace
140
         self.content = None
159
         self.content = None
160
+        self.user = UserApi(None).get_one_by_email(environ['http_authenticator.username'])
141
 
161
 
142
-        self.content_api = ContentApi(UserApi(None).get_one_by_email(environ['http_authenticator.username']))
162
+        self.content_api = ContentApi(self.user, show_temporary=True)
143
 
163
 
144
         self._file_count = 0
164
         self._file_count = 0
145
 
165
 
147
         return "<DAVCollection: Workspace (%d)>" % self.workspace.workspace_id
167
         return "<DAVCollection: Workspace (%d)>" % self.workspace.workspace_id
148
 
168
 
149
     def getPreferredPath(self):
169
     def getPreferredPath(self):
150
-        return self.provider.transform_to_display(self.path)
170
+        return self.path
151
 
171
 
152
     def getCreationDate(self) -> float:
172
     def getCreationDate(self) -> float:
153
         return mktime(self.workspace.created.timetuple())
173
         return mktime(self.workspace.created.timetuple())
177
     def getMember(self, content_label: str) -> _DAVResource:
197
     def getMember(self, content_label: str) -> _DAVResource:
178
 
198
 
179
         return self.provider.getResourceInst(
199
         return self.provider.getResourceInst(
180
-            '%s/%s' % (self.path, content_label),
200
+            '%s/%s' % (self.path, self.provider.transform_to_display(content_label)),
181
             self.environ
201
             self.environ
182
         )
202
         )
183
 
203
 
224
 
244
 
225
         transaction.commit()
245
         transaction.commit()
226
 
246
 
227
-        return Folder('%s/%s' % (self.path, label), self.environ, folder, self.workspace)
247
+        return Folder('%s/%s' % (self.path, self.provider.transform_to_display(label)),
248
+                      self.environ, folder,
249
+                      self.workspace)
228
 
250
 
229
     def delete(self):
251
     def delete(self):
230
         """For now, it is not possible to delete a workspace through the webdav client."""
252
         """For now, it is not possible to delete a workspace through the webdav client."""
231
         raise DAVError(HTTP_FORBIDDEN)
253
         raise DAVError(HTTP_FORBIDDEN)
232
 
254
 
233
-    def copyMoveSingle(self, destpath, ismove):
234
-        raise DAVError(HTTP_FORBIDDEN)
235
-
236
     def supportRecursiveMove(self, destpath):
255
     def supportRecursiveMove(self, destpath):
237
-        # return False
238
         return True
256
         return True
239
 
257
 
240
     def moveRecursive(self, destpath):
258
     def moveRecursive(self, destpath):
241
-        if dirname(normpath(destpath)) == '/':
259
+        if dirname(normpath(destpath)) == self.provider.root:
242
             self.workspace.label = basename(normpath(destpath))
260
             self.workspace.label = basename(normpath(destpath))
243
             transaction.commit()
261
             transaction.commit()
244
         else:
262
         else:
245
             raise DAVError(HTTP_FORBIDDEN)
263
             raise DAVError(HTTP_FORBIDDEN)
246
 
264
 
247
     def getMemberList(self) -> [_DAVResource]:
265
     def getMemberList(self) -> [_DAVResource]:
248
-        memberlist = []
266
+        members = []
267
+        parent_id = None if self.content is None else self.content.id
268
+
269
+        childs = self.content_api.get_all(parent_id, ContentType.Any, self.workspace)
249
 
270
 
250
-        for name in self.getMemberNames():
251
-            member = self.getMember(name)
252
-            if member is not None:
253
-                memberlist.append(member)
271
+        for content in childs:
272
+            content_path = '%s/%s' % (self.path, self.provider.transform_to_display(content.get_label()))
273
+
274
+            if content.type == ContentType.Folder:
275
+                members.append(Folder(content_path, self.environ, content, self.workspace))
276
+            elif content.type == ContentType.File:
277
+                self._file_count += 1
278
+                members.append(File(content_path, self.environ, content))
279
+            else:
280
+                self._file_count += 1
281
+                members.append(OtherFile(content_path, self.environ, content))
254
 
282
 
255
         if self._file_count > 0 and self.provider.show_history():
283
         if self._file_count > 0 and self.provider.show_history():
256
-            memberlist.append(
284
+            members.append(
257
                 HistoryFolder(
285
                 HistoryFolder(
258
                     path=self.path + '/' + ".history",
286
                     path=self.path + '/' + ".history",
259
                     environ=self.environ,
287
                     environ=self.environ,
264
             )
292
             )
265
 
293
 
266
         if self.provider.show_delete():
294
         if self.provider.show_delete():
267
-            memberlist.append(
295
+            members.append(
268
                 DeletedFolder(
296
                 DeletedFolder(
269
                     path=self.path + '/' + ".deleted",
297
                     path=self.path + '/' + ".deleted",
270
                     environ=self.environ,
298
                     environ=self.environ,
274
             )
302
             )
275
 
303
 
276
         if self.provider.show_archive():
304
         if self.provider.show_archive():
277
-            memberlist.append(
305
+            members.append(
278
                 ArchivedFolder(
306
                 ArchivedFolder(
279
                     path=self.path + '/' + ".archived",
307
                     path=self.path + '/' + ".archived",
280
                     environ=self.environ,
308
                     environ=self.environ,
282
                     workspace=self.workspace
310
                     workspace=self.workspace
283
                 )
311
                 )
284
             )
312
             )
285
-
286
-        return memberlist
313
+        return members
287
 
314
 
288
 
315
 
289
 class Folder(Workspace):
316
 class Folder(Workspace):
295
         super(Folder, self).__init__(path, environ, workspace)
322
         super(Folder, self).__init__(path, environ, workspace)
296
 
323
 
297
         self.content = content
324
         self.content = content
298
-        self.path = self.provider.transform_to_display(self.path)
299
-
300
-        self.current_path = 'DELETED' if basename(dirname(path)) == '.deleted' \
301
-            else 'ARCHIVED' if basename(dirname(path)) == '.archived' \
302
-            else ''
303
 
325
 
304
     def __repr__(self) -> str:
326
     def __repr__(self) -> str:
305
         return "<DAVCollection: Folder (%s)>" % self.content.label
327
         return "<DAVCollection: Folder (%s)>" % self.content.label
313
     def getLastModified(self) -> float:
335
     def getLastModified(self) -> float:
314
         return mktime(self.content.updated.timetuple())
336
         return mktime(self.content.updated.timetuple())
315
 
337
 
316
-    def handleDelete(self):
338
+    def delete(self):
317
         Manage(ActionDescription.DELETION, self.content_api, self.content).action()
339
         Manage(ActionDescription.DELETION, self.content_api, self.content).action()
318
-        return True
319
 
340
 
320
-    def delete(self):
321
-        raise DAVError(HTTP_FORBIDDEN)
322
-        
323
     def supportRecursiveMove(self, destpath: str):
341
     def supportRecursiveMove(self, destpath: str):
324
         return True
342
         return True
325
 
343
 
377
         parent = self.provider.get_parent_from_path(
395
         parent = self.provider.get_parent_from_path(
378
             normpath(destpath),
396
             normpath(destpath),
379
             self.content_api,
397
             self.content_api,
380
-            WorkspaceApi(self.environ['user']))
398
+            WorkspaceApi(self.user)
399
+        )
381
 
400
 
382
         workspace = self.provider.get_workspace_from_path(
401
         workspace = self.provider.get_workspace_from_path(
383
             normpath(destpath),
402
             normpath(destpath),
384
-            WorkspaceApi(self.environ['user'])
403
+            WorkspaceApi(self.user)
385
         )
404
         )
386
 
405
 
387
         with new_revision(self.content):
406
         with new_revision(self.content):
388
-            if basename(destpath) != self.content.label:
389
-                self.content_api.update_content(self.content, basename(destpath), self.content.description)
407
+            if basename(destpath) != self.getDisplayName():
408
+                self.content_api.update_content(self.content, self.provider.transform_to_bdd(basename(destpath)))
390
                 self.content_api.save(self.content)
409
                 self.content_api.save(self.content)
391
-
392
-            try:
393
-                workspace_id = parent.workspace.workspace_id
394
-            except AttributeError:
395
-                workspace_id = self.provider.get_workspace_from_path(
396
-                    destpath, WorkspaceApi(self.environ['user'])
397
-                ).workspace_id
398
-
399
-            if workspace_id == self.content.workspace.workspace_id:
400
-                self.content_api.move(self.content, parent)
401
             else:
410
             else:
402
                 try:
411
                 try:
403
-                    self.content_api.move_recursively(self.content, parent, parent.workspace)
412
+                    workspace_id = parent.workspace.workspace_id
404
                 except AttributeError:
413
                 except AttributeError:
405
-                    self.content_api.move_recursively(self.content, parent, workspace)
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:
419
+                    self.content_api.move(self.content, parent)
420
+                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)
406
 
425
 
407
         transaction.commit()
426
         transaction.commit()
408
 
427
 
416
         self._is_deleted = type == HistoryType.Deleted
435
         self._is_deleted = type == HistoryType.Deleted
417
 
436
 
418
         self.content_api = ContentApi(
437
         self.content_api = ContentApi(
419
-            current_user=UserApi(None).get_one_by_email(environ['http_authenticator.username']),
438
+            current_user=self.user,
420
             show_archived=self._is_archived,
439
             show_archived=self._is_archived,
421
             show_deleted=self._is_deleted
440
             show_deleted=self._is_deleted
422
         )
441
         )
447
     def getMemberNames(self) -> [str]:
466
     def getMemberNames(self) -> [str]:
448
         ret = []
467
         ret = []
449
 
468
 
450
-        for content in self.content_api.get_all(self.content.id, ContentType.Any):
469
+        content_id = None if self.content is None else self.content.id
470
+        for content in self.content_api.get_all(content_id, ContentType.Any, self.workspace):
451
             if (self._is_archived and content.is_archived or
471
             if (self._is_archived and content.is_archived or
452
                 self._is_deleted and content.is_deleted or
472
                 self._is_deleted and content.is_deleted or
453
                 not (content.is_archived or self._is_archived or content.is_deleted or self._is_deleted))\
473
                 not (content.is_archived or self._is_archived or content.is_deleted or self._is_deleted))\
475
         return True
495
         return True
476
 
496
 
477
     def getMemberList(self) -> [_DAVResource]:
497
     def getMemberList(self) -> [_DAVResource]:
478
-        memberlist = []
479
-        for name in self.getMemberNames():
480
-            member = self.getMember(name)
481
-            if member is not None:
482
-                memberlist.append(member)
483
-        return memberlist
498
+        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):
503
+            members.append(HistoryFileFolder(
504
+                path='%s/%s' % (self.path, content.get_label()),
505
+                environ=self.environ,
506
+                content=content))
507
+
508
+        return members
484
 
509
 
485
 
510
 
486
 class DeletedFolder(HistoryFolder):
511
 class DeletedFolder(HistoryFolder):
487
     def __init__(self, path: str, environ: dict, workspace: data.Workspace, content: data.Content=None):
512
     def __init__(self, path: str, environ: dict, workspace: data.Workspace, content: data.Content=None):
488
         super(DeletedFolder, self).__init__(path, environ, workspace, HistoryType.Deleted, content)
513
         super(DeletedFolder, self).__init__(path, environ, workspace, HistoryType.Deleted, content)
489
 
514
 
490
-        self.content_api = ContentApi(
491
-            current_user=environ['user'],
492
-            show_deleted=True
493
-        )
494
-
495
         self._file_count = 0
515
         self._file_count = 0
496
 
516
 
497
     def __repr__(self):
517
     def __repr__(self):
514
         )
534
         )
515
 
535
 
516
         return self.provider.getResourceInst(
536
         return self.provider.getResourceInst(
517
-            path='%s/%s' % (self.path, content.get_label()),
537
+            path='%s/%s' % (self.path, self.provider.transform_to_display(content.get_label())),
518
             environ=self.environ
538
             environ=self.environ
519
             )
539
             )
520
 
540
 
541
         raise DAVError(HTTP_FORBIDDEN)
561
         raise DAVError(HTTP_FORBIDDEN)
542
 
562
 
543
     def getMemberList(self) -> [_DAVResource]:
563
     def getMemberList(self) -> [_DAVResource]:
544
-        memberlist = []
564
+        members = []
565
+
566
+        parent_id = None if self.content is None else self.content.id
567
+
568
+        for content in self.content_api.get_all_with_filter(parent_id, ContentType.Any, self.workspace):
569
+            content_path = '%s/%s' % (self.path, self.provider.transform_to_display(content.get_label()))
545
 
570
 
546
-        for name in self.getMemberNames():
547
-            member = self.getMember(name)
548
-            if member is not None:
549
-                memberlist.append(member)
571
+            if content.type == ContentType.Folder:
572
+                members.append(Folder(content_path, self.environ, content, self.workspace))
573
+            elif content.type == ContentType.File:
574
+                self._file_count += 1
575
+                members.append(File(content_path, self.environ, content))
576
+            else:
577
+                self._file_count += 1
578
+                members.append(OtherFile(content_path, self.environ, content))
550
 
579
 
551
         if self._file_count > 0 and self.provider.show_history():
580
         if self._file_count > 0 and self.provider.show_history():
552
-            memberlist.append(
581
+            members.append(
553
                 HistoryFolder(
582
                 HistoryFolder(
554
                     path=self.path + '/' + ".history",
583
                     path=self.path + '/' + ".history",
555
                     environ=self.environ,
584
                     environ=self.environ,
556
                     content=self.content,
585
                     content=self.content,
557
-                    type=HistoryType.Deleted,
558
-                    workspace=self.workspace
586
+                    workspace=self.workspace,
587
+                    type=HistoryType.Standard
559
                 )
588
                 )
560
             )
589
             )
561
 
590
 
562
-        return memberlist
591
+        return members
563
 
592
 
564
 
593
 
565
 class ArchivedFolder(HistoryFolder):
594
 class ArchivedFolder(HistoryFolder):
566
     def __init__(self, path: str, environ: dict, workspace: data.Workspace, content: data.Content=None):
595
     def __init__(self, path: str, environ: dict, workspace: data.Workspace, content: data.Content=None):
567
         super(ArchivedFolder, self).__init__(path, environ, workspace, HistoryType.Archived, content)
596
         super(ArchivedFolder, self).__init__(path, environ, workspace, HistoryType.Archived, content)
568
 
597
 
569
-        self.content_api = ContentApi(
570
-            current_user=environ['user'],
571
-            show_archived=True
572
-        )
573
-
574
         self._file_count = 0
598
         self._file_count = 0
575
 
599
 
576
     def __repr__(self) -> str:
600
     def __repr__(self) -> str:
593
         )
617
         )
594
 
618
 
595
         return self.provider.getResourceInst(
619
         return self.provider.getResourceInst(
596
-            path=self.path + '/' + content.get_label(),
620
+            path=self.path + '/' + self.provider.transform_to_display(content.get_label()),
597
             environ=self.environ
621
             environ=self.environ
598
-            )
622
+        )
599
 
623
 
600
     def getMemberNames(self) -> [str]:
624
     def getMemberNames(self) -> [str]:
601
         retlist = []
625
         retlist = []
602
 
626
 
603
-        for content in self.content_api.get_all(
627
+        for content in self.content_api.get_all_with_filter(
604
                 self.content if self.content is None else self.content.id, ContentType.Any):
628
                 self.content if self.content is None else self.content.id, ContentType.Any):
605
-            if content.is_archived:
606
-                retlist.append(content.get_label())
629
+            retlist.append(content.get_label())
607
 
630
 
608
-                if content.type != ContentType.Folder:
609
-                    self._file_count += 1
631
+            if content.type != ContentType.Folder:
632
+                self._file_count += 1
610
 
633
 
611
         return retlist
634
         return retlist
612
 
635
 
620
         raise DAVError(HTTP_FORBIDDEN)
643
         raise DAVError(HTTP_FORBIDDEN)
621
 
644
 
622
     def getMemberList(self) -> [_DAVResource]:
645
     def getMemberList(self) -> [_DAVResource]:
623
-        memberlist = []
646
+        members = []
624
 
647
 
625
-        for name in self.getMemberNames():
626
-            member = self.getMember(name)
627
-            if member is not None:
628
-                memberlist.append(member)
648
+        parent_id = None if self.content is None else self.content.id
649
+
650
+        for content in self.content_api.get_all_with_filter(parent_id, ContentType.Any, self.workspace):
651
+            content_path = '%s/%s' % (self.path, self.provider.transform_to_display(content.get_label()))
652
+
653
+            if content.type == ContentType.Folder:
654
+                members.append(Folder(content_path, self.environ, content, self.workspace))
655
+            elif content.type == ContentType.File:
656
+                self._file_count += 1
657
+                members.append(File(content_path, self.environ, content))
658
+            else:
659
+                self._file_count += 1
660
+                members.append(OtherFile(content_path, self.environ, content))
629
 
661
 
630
         if self._file_count > 0 and self.provider.show_history():
662
         if self._file_count > 0 and self.provider.show_history():
631
-            memberlist.append(
663
+            members.append(
632
                 HistoryFolder(
664
                 HistoryFolder(
633
                     path=self.path + '/' + ".history",
665
                     path=self.path + '/' + ".history",
634
                     environ=self.environ,
666
                     environ=self.environ,
635
                     content=self.content,
667
                     content=self.content,
636
-                    type=HistoryType.Archived,
637
-                    workspace=self.workspace
668
+                    workspace=self.workspace,
669
+                    type=HistoryType.Standard
638
                 )
670
                 )
639
             )
671
             )
640
 
672
 
641
-        return memberlist
673
+        return members
642
 
674
 
643
 
675
 
644
 class HistoryFileFolder(HistoryFolder):
676
 class HistoryFileFolder(HistoryFolder):
663
         )
695
         )
664
 
696
 
665
     def getMemberNames(self) -> [int]:
697
     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"""
666
         ret = []
699
         ret = []
667
 
700
 
668
         for content in self.content.revisions:
701
         for content in self.content.revisions:
669
-            if content.revision_type in \
670
-                    [ActionDescription.CREATION, ActionDescription.EDITION, ActionDescription.REVISION]:
671
-                ret.append(content.revision_id)
702
+            ret.append(content.revision_id)
672
 
703
 
673
         return ret
704
         return ret
674
 
705
 
675
     def getMember(self, item_id) -> DAVCollection:
706
     def getMember(self, item_id) -> DAVCollection:
676
 
707
 
677
         revision = self.content_api.get_one_revision(item_id)
708
         revision = self.content_api.get_one_revision(item_id)
678
-        
709
+
710
+        left_side = '%s/(%d - %s) ' % (self.path, revision.revision_id, revision.revision_type)
711
+
679
         if self.content.type == ContentType.File:
712
         if self.content.type == ContentType.File:
680
             return HistoryFile(
713
             return HistoryFile(
681
-                path=self.path + '/' + str(revision.revision_id) + '-' + revision.file_name,
714
+                path='%s%s' % (left_side, self.provider.transform_to_display(revision.file_name)),
682
                 environ=self.environ,
715
                 environ=self.environ,
683
-                content=self.content, 
716
+                content=self.content,
684
                 content_revision=revision)
717
                 content_revision=revision)
685
         else:
718
         else:
686
             return HistoryOtherFile(
719
             return HistoryOtherFile(
687
-                path=self.path + '/' + str(revision.revision_id) + '-' + revision.get_label(),
720
+                path='%s%s' % (left_side, self.provider.transform_to_display(revision.get_label())),
688
                 environ=self.environ,
721
                 environ=self.environ,
689
                 content=self.content,
722
                 content=self.content,
690
                 content_revision=revision)
723
                 content_revision=revision)
693
         raise DAVError(HTTP_FORBIDDEN)
726
         raise DAVError(HTTP_FORBIDDEN)
694
 
727
 
695
     def getMemberList(self) -> [_DAVResource]:
728
     def getMemberList(self) -> [_DAVResource]:
696
-        memberlist = []
729
+        members = []
730
+
731
+        for content in self.content.revisions:
732
+
733
+            left_side = '%s/(%d - %s) ' % (self.path, content.revision_id, content.revision_type)
697
 
734
 
698
-        for name in self.getMemberNames():
699
-            member = self.getMember(name)
700
-            if member is not None:
701
-                memberlist.append(member)
735
+            if self.content.type == ContentType.File:
736
+                members.append(HistoryFile(
737
+                    path='%s%s' % (left_side, self.provider.transform_to_display(content.file_name)),
738
+                    environ=self.environ,
739
+                    content=self.content,
740
+                    content_revision=content)
741
+                )
742
+            else:
743
+                members.append(HistoryOtherFile(
744
+                    path='%s%s' % (left_side, self.provider.transform_to_display(content.file_name)),
745
+                    environ=self.environ,
746
+                    content=self.content,
747
+                    content_revision=content)
748
+                )
702
 
749
 
703
-        return memberlist
750
+        return members
704
 
751
 
705
 
752
 
706
 class File(DAVNonCollection):
753
 class File(DAVNonCollection):
708
         super(File, self).__init__(path, environ)
755
         super(File, self).__init__(path, environ)
709
 
756
 
710
         self.content = content
757
         self.content = content
758
+        self.user = UserApi(None).get_one_by_email(environ['http_authenticator.username'])
759
+        self.content_api = ContentApi(self.user)
711
 
760
 
712
-        self.content_api = ContentApi(
713
-            current_user=environ['user'],
714
-            show_archived=True,
715
-            show_deleted=True
716
-        )
717
 
761
 
718
     def getPreferredPath(self):
762
     def getPreferredPath(self):
719
-        if self.content.label == '' or self.path.endswith(mimetypes.guess_extension(self.getContentType())):
720
-            return self.provider.transform_to_display(self.path)
763
+        fix_txt = '.txt' if self.getContentType() == 'text/plain' else mimetypes.guess_extension(self.getContentType())
764
+
765
+        if self.content.label == '' or self.path.endswith(fix_txt):
766
+            return self.path
721
         else:
767
         else:
722
-            return self.provider.transform_to_display(self.path + mimetypes.guess_extension(self.getContentType()))
768
+            return self.path + fix_txt
723
 
769
 
724
     def __repr__(self) -> str:
770
     def __repr__(self) -> str:
725
         return "<DAVNonCollection: File (%d)>" % self.content.revision_id
771
         return "<DAVNonCollection: File (%d)>" % self.content.revision_id
734
         return mktime(self.content.created.timetuple())
780
         return mktime(self.content.created.timetuple())
735
 
781
 
736
     def getDisplayName(self) -> str:
782
     def getDisplayName(self) -> str:
737
-        return '%s%s' % (self.content.get_label(), '')
738
-        # ''.%s' % self.getContentType() if self.content.label != '' else '')
783
+        return self.content.get_label()
739
 
784
 
740
     def getLastModified(self) -> float:
785
     def getLastModified(self) -> float:
741
         return mktime(self.content.updated.timetuple())
786
         return mktime(self.content.updated.timetuple())
755
             workspace=self.content.workspace
800
             workspace=self.content.workspace
756
         )
801
         )
757
 
802
 
758
-    def copyMoveSingle(self, destpath: str, ismove: bool):
759
-        pass #if we use that to move items it'll first call delete function and we get exception about content not
760
-        # linked to a session, so for now we use moveRecursive
761
-
762
     def moveRecursive(self, destpath):
803
     def moveRecursive(self, destpath):
763
         """As we support recursive move, copymovesingle won't be called, though with copy it'll be called
804
         """As we support recursive move, copymovesingle won't be called, though with copy it'll be called
764
             but i have to check if the client ever call that function..."""
805
             but i have to check if the client ever call that function..."""
813
         parent = self.provider.get_parent_from_path(
854
         parent = self.provider.get_parent_from_path(
814
             normpath(destpath),
855
             normpath(destpath),
815
             self.content_api,
856
             self.content_api,
816
-            WorkspaceApi(self.environ['user'])
857
+            WorkspaceApi(self.user)
817
         )
858
         )
818
 
859
 
819
         workspace = self.provider.get_workspace_from_path(
860
         workspace = self.provider.get_workspace_from_path(
820
             normpath(destpath),
861
             normpath(destpath),
821
-            WorkspaceApi(self.environ['user'])
862
+            WorkspaceApi(self.user)
822
         )
863
         )
823
 
864
 
824
         with new_revision(self.content):
865
         with new_revision(self.content):
825
-            if basename(destpath) != self.content.label:
826
-                self.content_api.update_content(self.content, basename(destpath), self.content.description)
866
+            if basename(destpath) != self.getDisplayName():
867
+                self.content_api.update_content(self.content, re.sub('\.[^\.]+$', '', self.provider.transform_to_bdd(basename(destpath))))
827
                 self.content_api.save(self.content)
868
                 self.content_api.save(self.content)
828
-
829
-            self.content_api.move(
830
-                item=self.content,
831
-                new_parent=parent,
832
-                must_stay_in_same_workspace=False,
833
-                new_workspace=workspace
834
-            )
869
+            else:
870
+                self.content_api.move(
871
+                    item=self.content,
872
+                    new_parent=parent,
873
+                    must_stay_in_same_workspace=False,
874
+                    new_workspace=workspace
875
+                )
835
 
876
 
836
         transaction.commit()
877
         transaction.commit()
837
 
878
 
851
         return "<DAVNonCollection: HistoryFile (%s-%s)" % (self.content.content_id, self.content.file_name)
892
         return "<DAVNonCollection: HistoryFile (%s-%s)" % (self.content.content_id, self.content.file_name)
852
 
893
 
853
     def getDisplayName(self) -> str:
894
     def getDisplayName(self) -> str:
854
-        return str(self.content_revision.revision_id) + '-' + self.content_revision.file_name
895
+        left_side = '(%d - %s) ' % (self.content_revision.revision_id, self.content_revision.revision_type)
896
+        return '%s%s' % (left_side, self.provider.transform_to_display(self.content_revision.file_name))
855
 
897
 
856
     def getContent(self):
898
     def getContent(self):
857
         filestream = compat.BytesIO()
899
         filestream = compat.BytesIO()
893
 
935
 
894
         self.content_designed = self.design()
936
         self.content_designed = self.design()
895
 
937
 
938
+        # workaroung for consistent request as we have to return a resource with a path ending with .html
939
+        # when entering folder for windows, but only once because when we select it again it would have .html.html
940
+        # which is no good
941
+        if not self.path.endswith('.html'):
942
+            self.path += '.html'
943
+
896
     def getDisplayName(self) -> str:
944
     def getDisplayName(self) -> str:
897
         return self.content.get_label()
945
         return self.content.get_label()
898
 
946
 
899
     def getPreferredPath(self):
947
     def getPreferredPath(self):
900
-        return self.path + '.html'
948
+        return self.path
901
 
949
 
902
     def __repr__(self) -> str:
950
     def __repr__(self) -> str:
903
         return "<DAVNonCollection: OtherFile (%s)" % self.content.file_name
951
         return "<DAVNonCollection: OtherFile (%s)" % self.content.file_name
917
 
965
 
918
     def design(self):
966
     def design(self):
919
         if self.content.type == ContentType.Page:
967
         if self.content.type == ContentType.Page:
920
-            return designPage(self.content, self.content.revision)
968
+            return designPage(self.content, self.content_revision)
921
         else:
969
         else:
922
             return designThread(
970
             return designThread(
923
                 self.content,
971
                 self.content,
930
     def __init__(self, path: str, environ: dict, content: data.Content, content_revision: data.ContentRevisionRO):
978
     def __init__(self, path: str, environ: dict, content: data.Content, content_revision: data.ContentRevisionRO):
931
         super(HistoryOtherFile, self).__init__(path, environ, content)
979
         super(HistoryOtherFile, self).__init__(path, environ, content)
932
         self.content_revision = content_revision
980
         self.content_revision = content_revision
981
+        self.content_designed = self.design()
933
 
982
 
934
     def __repr__(self) -> str:
983
     def __repr__(self) -> str:
935
         return "<DAVNonCollection: HistoryOtherFile (%s-%s)" % (self.content.file_name, self.content.id)
984
         return "<DAVNonCollection: HistoryOtherFile (%s-%s)" % (self.content.file_name, self.content.id)
936
 
985
 
937
     def getDisplayName(self) -> str:
986
     def getDisplayName(self) -> str:
938
-        return str(self.content_revision.revision_id) + '-' + self.content_revision.get_label()
987
+        left_side = '(%d - %s) ' % (self.content_revision.revision_id, self.content_revision.revision_type)
988
+        return '%s%s' % (left_side, self.provider.transform_to_display(self.content_revision.get_label()))
939
 
989
 
940
     def getContent(self):
990
     def getContent(self):
941
         filestream = compat.BytesIO()
991
         filestream = compat.BytesIO()
963
     def copyMoveSingle(self, destpath, ismove):
1013
     def copyMoveSingle(self, destpath, ismove):
964
         raise DAVError(HTTP_FORBIDDEN)
1014
         raise DAVError(HTTP_FORBIDDEN)
965
 
1015
 
966
-
967
-_CONTENTS = {
968
-    ContentType.Folder: Folder,
969
-    ContentType.File: File,
970
-    ContentType.Page: OtherFile,
971
-    ContentType.Thread: OtherFile
972
-}

+ 2 - 1
tracim/tracim/lib/webdav/tracim_http_authenticator.py 查看文件

5
 _logger = util.getModuleLogger(__name__, True)
5
 _logger = util.getModuleLogger(__name__, True)
6
 HOTFIX_WINXP_AcceptRootShareLogin = True
6
 HOTFIX_WINXP_AcceptRootShareLogin = True
7
 
7
 
8
+
8
 class TracimHTTPAuthenticator(HTTPAuthenticator):
9
 class TracimHTTPAuthenticator(HTTPAuthenticator):
9
     def __init__(self, application, config):
10
     def __init__(self, application, config):
10
         super(TracimHTTPAuthenticator, self).__init__(application, config)
11
         super(TracimHTTPAuthenticator, self).__init__(application, config)
58
             # TODO: Chun added this comments, but code was commented out
59
             # TODO: Chun added this comments, but code was commented out
59
             # Do not do realm checking - a hotfix for WinXP using some other realm's
60
             # Do not do realm checking - a hotfix for WinXP using some other realm's
60
             # auth details for this realm - if user/password match
61
             # auth details for this realm - if user/password match
61
-        print(authheaderdict.get("realm"), realmname)
62
+
62
         if 'realm' in authheaderdict:
63
         if 'realm' in authheaderdict:
63
             if authheaderdict["realm"].upper() != realmname.upper():
64
             if authheaderdict["realm"].upper() != realmname.upper():
64
                 if HOTFIX_WINXP_AcceptRootShareLogin:
65
                 if HOTFIX_WINXP_AcceptRootShareLogin:

+ 13 - 0
tracim/tracim/model/data.py 查看文件

533
     updated = Column(DateTime, unique=False, nullable=False, default=datetime.utcnow)
533
     updated = Column(DateTime, unique=False, nullable=False, default=datetime.utcnow)
534
     is_deleted = Column(Boolean, unique=False, nullable=False, default=False)
534
     is_deleted = Column(Boolean, unique=False, nullable=False, default=False)
535
     is_archived = Column(Boolean, unique=False, nullable=False, default=False)
535
     is_archived = Column(Boolean, unique=False, nullable=False, default=False)
536
+    is_temporary = Column(Boolean, unique=False, nullable=False, default=False)
536
     revision_type = Column(Unicode(32), unique=False, nullable=False, default='')
537
     revision_type = Column(Unicode(32), unique=False, nullable=False, default='')
537
 
538
 
538
     workspace_id = Column(Integer, ForeignKey('workspaces.workspace_id'), unique=False, nullable=True)
539
     workspace_id = Column(Integer, ForeignKey('workspaces.workspace_id'), unique=False, nullable=True)
852
         return ContentRevisionRO.is_archived
853
         return ContentRevisionRO.is_archived
853
 
854
 
854
     @hybrid_property
855
     @hybrid_property
856
+    def is_temporary(self) -> bool:
857
+        return self.revision.is_temporary
858
+
859
+    @is_temporary.setter
860
+    def is_temporary(self, value: bool) -> None:
861
+        self.revision.is_temporary = value
862
+
863
+    @is_temporary.expression
864
+    def is_temporary(cls) -> InstrumentedAttribute:
865
+        return ContentRevisionRO.is_temporary
866
+
867
+    @hybrid_property
855
     def revision_type(self) -> str:
868
     def revision_type(self) -> str:
856
         return self.revision.revision_type
869
         return self.revision.revision_type
857
 
870