Explorar el Código

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

Nonolost hace 10 años
padre
commit
bb447d7988

+ 8 - 2
tracim/migration/versions/b4b8d57b54e5_add_hash_column_for_digest_.py Ver fichero

@@ -11,12 +11,18 @@ revision = 'b4b8d57b54e5'
11 11
 down_revision = '534c4594ed29'
12 12
 
13 13
 from alembic import op
14
-from sqlalchemy import Column, Unicode
14
+from sqlalchemy import Column, Unicode, Boolean
15 15
 
16 16
 
17 17
 def upgrade():
18 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 26
 def downgrade():
22 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 Ver fichero

@@ -80,6 +80,7 @@ class ContentApi(object):
80 80
             current_user: User,
81 81
             show_archived=False,
82 82
             show_deleted=False,
83
+            show_temporary=False,
83 84
             all_content_in_treeview=True,
84 85
             force_show_all_types=False,
85 86
             disable_user_workspaces_filter=False,
@@ -88,6 +89,7 @@ class ContentApi(object):
88 89
         self._user_id = current_user.user_id if current_user else None
89 90
         self._show_archived = show_archived
90 91
         self._show_deleted = show_deleted
92
+        self._show_temporary = show_temporary
91 93
         self._show_all_type_of_contents_in_treeview = all_content_in_treeview
92 94
         self._force_show_all_types = force_show_all_types
93 95
         self._disable_user_workspaces_filter = disable_user_workspaces_filter
@@ -200,6 +202,9 @@ class ContentApi(object):
200 202
         if not self._show_archived:
201 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 208
         return result
204 209
 
205 210
     def __revisions_real_base_query(self, workspace: Workspace=None):
@@ -230,6 +235,9 @@ class ContentApi(object):
230 235
         if not self._show_archived:
231 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 241
         return result
234 242
 
235 243
     def _hard_filtered_base_query(self, workspace: Workspace=None):
@@ -256,6 +264,12 @@ class ContentApi(object):
256 264
                 filter(Content.is_archived==False).\
257 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 273
         return result
260 274
 
261 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,7 +316,7 @@ class ContentApi(object):
302 316
 
303 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 320
         assert content_type in ContentType.allowed_types()
307 321
         content = Content()
308 322
         content.owner = self._user
@@ -310,6 +324,7 @@ class ContentApi(object):
310 324
         content.workspace = workspace
311 325
         content.type = content_type
312 326
         content.label = label
327
+        content.is_temporary = is_temporary
313 328
         content.revision_type = ActionDescription.CREATION
314 329
 
315 330
         if do_save:
@@ -416,6 +431,25 @@ class ContentApi(object):
416 431
 
417 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 453
     def get_all_without_exception(self, content_type: str, workspace: Workspace=None) -> [Content]:
420 454
         assert content_type is not None# DYN_REMOVE
421 455
 

+ 5 - 2
tracim/tracim/lib/webdav/__init__.py Ver fichero

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

+ 26 - 17
tracim/tracim/lib/webdav/design.py Ver fichero

@@ -66,7 +66,7 @@ def designPage(content: data.Content, content_revision: data.ContentRevisionRO)
66 66
                        date,
67 67
                        event.owner.display_name,
68 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 71
     histHTML += '</table>'
72 72
 
@@ -78,6 +78,10 @@ def designPage(content: data.Content, content_revision: data.ContentRevisionRO)
78 78
 	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
79 79
 	<link rel="stylesheet" href="/home/arnaud/Documents/css/style.css">
80 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 85
 </head>
82 86
 <body>
83 87
     <div id="left" class="col-lg-8 col-md-12 col-sm-12 col-xs-12">
@@ -105,12 +109,13 @@ def designPage(content: data.Content, content_revision: data.ContentRevisionRO)
105 109
     </div>
106 110
     <script type="text/javascript">
107 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 120
     </script>
116 121
 </body>
@@ -173,7 +178,7 @@ def designThread(content: data.Content, content_revision: data.ContentRevisionRO
173 178
                     label = _LABELS[t.type.id]
174 179
 
175 180
                     disc += '''
176
-                    <div class="row comment comment-row to-hide">
181
+                    <div class="%s row comment comment-row to-hide">
177 182
                         <i class="fa %s comment-icon"></i>
178 183
                             <div class="comment-content">
179 184
                             <h5>
@@ -183,12 +188,13 @@ def designThread(content: data.Content, content_revision: data.ContentRevisionRO
183 188
                             %s %s
184 189
                         </div>
185 190
                     </div>
186
-                    ''' % (t.type.icon,
191
+                    ''' % ('warning' if t.id == content_revision.revision_id else '',
192
+                           t.type.icon,
187 193
                            t.owner.display_name,
188 194
                            t.create_readable_date(),
189 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 198
                                t.id,
193 199
                                t.ref_object.label) if t.type.id in ['revision', 'creation', 'edition'] else '')
194 200
 
@@ -230,12 +236,13 @@ def designThread(content: data.Content, content_revision: data.ContentRevisionRO
230 236
     </div>
231 237
     <script type="text/javascript">
232 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 248
         function hide_elements() {
@@ -246,6 +253,7 @@ def designThread(content: data.Content, content_revision: data.ContentRevisionRO
246 253
                     $(elems[i]).hide();
247 254
                 }
248 255
                 while (elems.length>0) {
256
+                    $(elems[0]).removeClass('comment-row');
249 257
                     $(elems[0]).removeClass('to-hide');
250 258
                 }
251 259
                 $('#hideshow').addClass('fa-eye').removeClass('fa-eye-slash');
@@ -254,6 +262,7 @@ def designThread(content: data.Content, content_revision: data.ContentRevisionRO
254 262
             else {
255 263
                 elems = document.getElementsByClassName('to-show');
256 264
                 for(var i = 0; i<elems.length; i++) {
265
+                    $(elems[0]).addClass('comment-row');
257 266
                     $(elems[i]).addClass('to-hide');
258 267
                     $(elems[i]).show();
259 268
                 }

+ 18 - 25
tracim/tracim/lib/webdav/sql_dav_provider.py Ver fichero

@@ -61,24 +61,21 @@ class Provider(DAVProvider):
61 61
     #########################################################
62 62
     # Everything override from DAVProvider
63 63
     def getResourceInst(self, path, environ):
64
-        print("ok : ", path)
65 64
         #if not self.exists(path, environ):
66 65
         #    return None
67 66
         if not self.exists(path, environ):
68 67
             return None
69 68
 
70
-        uapi = UserApi(None)
71
-        environ['user'] = uapi.get_one_by_email(environ['http_authenticator.username'])
72
-
73 69
         norm_path = normpath(path)
74
-        norm_path = self.transform_to_display(norm_path)
75 70
 
76 71
         root_path = environ['http_authenticator.realm']
77 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 77
         content_api = ContentApi(
81
-            environ['user'],
78
+            user,
82 79
             show_archived=self._show_archive,
83 80
             show_deleted=self._show_delete
84 81
         )
@@ -155,47 +152,43 @@ class Provider(DAVProvider):
155 152
             )
156 153
 
157 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 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 159
             content = self.get_content_from_revision(content_revision, content_api)
163 160
 
164 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 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 166
         # other
170 167
         if content is None:
171 168
             return None
172 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 171
         elif content.type == ContentType.File:
175
-            return sql_resources.File(path, environ, content)
172
+            return sql_resources.File(norm_path, environ, content)
176 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 175
         else:
179 176
             return None
180 177
 
181 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 179
         norm_path = normpath(path)
187 180
         parent_path = dirname(norm_path)
188 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 185
         content_api = ContentApi(
192
-            current_user=environ['user'],
186
+            current_user=user,
193 187
             show_archived=True,
194 188
             show_deleted=True
195 189
         )
196 190
 
197 191
         if path == root_path:
198
-            print("ok (pass ici) : ", path)
199 192
             return True
200 193
         elif parent_path == root_path:
201 194
             return self.get_workspace_from_path(
@@ -207,7 +200,7 @@ class Provider(DAVProvider):
207 200
 
208 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 205
         blbl = self.reduce_path(norm_path)
213 206
         if dirname(blbl) == '/':
@@ -238,7 +231,7 @@ class Provider(DAVProvider):
238 231
         workspace = self.get_workspace_from_path(path, workspace_api)
239 232
 
240 233
         try:
241
-            if basename(dirname(path)) == workspace.label:
234
+            if dirname(dirname(path)) == '/':
242 235
                 return content_api.get_one_by_label_and_parent(
243 236
                     self.transform_to_bdd(basename(path)),
244 237
                     workspace=workspace
@@ -271,7 +264,7 @@ class Provider(DAVProvider):
271 264
 
272 265
     def transform_to_display(self, string):
273 266
         _TO_DISPLAY = {
274
-            # '/':'⁄',
267
+            '/':'⧸',
275 268
             '\\': '⧹',
276 269
             ':': '∶',
277 270
             '*': '∗',
@@ -289,7 +282,7 @@ class Provider(DAVProvider):
289 282
 
290 283
     def transform_to_bdd(self, string):
291 284
         _TO_BDD = {
292
-            # '⁄': '/',
285
+            '⧸': '/',
293 286
             '⧹': '\\',
294 287
             '∶': ':',
295 288
             '∗': '*',

+ 3 - 0
tracim/tracim/lib/webdav/sql_domain_controller.py Ver fichero

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

+ 199 - 156
tracim/tracim/lib/webdav/sql_resources.py Ver fichero

@@ -7,6 +7,7 @@ from os.path import normpath, dirname, basename
7 7
 import mimetypes
8 8
 
9 9
 from tracim.lib.content import ContentApi
10
+from tracim.lib.user import UserApi
10 11
 from tracim.lib.webdav import HistoryType
11 12
 from tracim.lib.webdav import FakeFileStream
12 13
 from tracim.lib.workspace import WorkspaceApi
@@ -14,13 +15,13 @@ from tracim.model import data, new_revision
14 15
 from tracim.model.data import Content, ActionDescription
15 16
 from tracim.model.data import ContentType
16 17
 from tracim.lib.webdav.design import designThread, designPage
17
-from tracim.lib.user import UserApi
18 18
 
19 19
 from wsgidav import compat
20 20
 from wsgidav.dav_error import DAVError, HTTP_FORBIDDEN
21 21
 from wsgidav.dav_provider import DAVCollection, DAVNonCollection
22 22
 from wsgidav.dav_provider import _DAVResource
23 23
 
24
+from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
24 25
 
25 26
 class Manage(object):
26 27
     """Objet qui sert à encapsuler l'exécution des actions de l'api archive/delete..."""
@@ -44,18 +45,17 @@ class Manage(object):
44 45
         self._new_name = self.make_name()
45 46
 
46 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 60
     def make_name(self):
61 61
         """Créer le nouveau nom : rajoute de - archive date / retrait de - archive date suivant l'action"""
@@ -63,12 +63,13 @@ class Manage(object):
63 63
         extension = ''
64 64
         is_file_name = self.content.label == ''
65 65
 
66
+
66 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 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 73
         else:
73 74
             new_name = re.sub(r'( - (%s|%s) the .*)$' % (self._to_name[ActionDescription.DELETION], self._to_name[ActionDescription.ARCHIVING]), '', new_name)
74 75
 
@@ -83,7 +84,8 @@ class Root(DAVCollection):
83 84
     def __init__(self, path: str, environ: dict):
84 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 90
     def __repr__(self) -> str:
89 91
         return '<DAVCollection: Root>'
@@ -100,7 +102,7 @@ class Root(DAVCollection):
100 102
         """
101 103
         try:
102 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 107
             return Workspace(workspace_path, self.environ, workspace)
106 108
         except AttributeError:
@@ -122,11 +124,28 @@ class Root(DAVCollection):
122 124
         [For now] we don't allow to create new workspaces through
123 125
         webdav client. Though if we come to allow it, deleting the error's raise will
124 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 130
         new_workspace = self.workspace_api.create_workspace(name)
128 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 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 151
 class Workspace(DAVCollection):
@@ -138,8 +157,9 @@ class Workspace(DAVCollection):
138 157
 
139 158
         self.workspace = workspace
140 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 164
         self._file_count = 0
145 165
 
@@ -147,7 +167,7 @@ class Workspace(DAVCollection):
147 167
         return "<DAVCollection: Workspace (%d)>" % self.workspace.workspace_id
148 168
 
149 169
     def getPreferredPath(self):
150
-        return self.provider.transform_to_display(self.path)
170
+        return self.path
151 171
 
152 172
     def getCreationDate(self) -> float:
153 173
         return mktime(self.workspace.created.timetuple())
@@ -177,7 +197,7 @@ class Workspace(DAVCollection):
177 197
     def getMember(self, content_label: str) -> _DAVResource:
178 198
 
179 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 201
             self.environ
182 202
         )
183 203
 
@@ -224,36 +244,44 @@ class Workspace(DAVCollection):
224 244
 
225 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 251
     def delete(self):
230 252
         """For now, it is not possible to delete a workspace through the webdav client."""
231 253
         raise DAVError(HTTP_FORBIDDEN)
232 254
 
233
-    def copyMoveSingle(self, destpath, ismove):
234
-        raise DAVError(HTTP_FORBIDDEN)
235
-
236 255
     def supportRecursiveMove(self, destpath):
237
-        # return False
238 256
         return True
239 257
 
240 258
     def moveRecursive(self, destpath):
241
-        if dirname(normpath(destpath)) == '/':
259
+        if dirname(normpath(destpath)) == self.provider.root:
242 260
             self.workspace.label = basename(normpath(destpath))
243 261
             transaction.commit()
244 262
         else:
245 263
             raise DAVError(HTTP_FORBIDDEN)
246 264
 
247 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 283
         if self._file_count > 0 and self.provider.show_history():
256
-            memberlist.append(
284
+            members.append(
257 285
                 HistoryFolder(
258 286
                     path=self.path + '/' + ".history",
259 287
                     environ=self.environ,
@@ -264,7 +292,7 @@ class Workspace(DAVCollection):
264 292
             )
265 293
 
266 294
         if self.provider.show_delete():
267
-            memberlist.append(
295
+            members.append(
268 296
                 DeletedFolder(
269 297
                     path=self.path + '/' + ".deleted",
270 298
                     environ=self.environ,
@@ -274,7 +302,7 @@ class Workspace(DAVCollection):
274 302
             )
275 303
 
276 304
         if self.provider.show_archive():
277
-            memberlist.append(
305
+            members.append(
278 306
                 ArchivedFolder(
279 307
                     path=self.path + '/' + ".archived",
280 308
                     environ=self.environ,
@@ -282,8 +310,7 @@ class Workspace(DAVCollection):
282 310
                     workspace=self.workspace
283 311
                 )
284 312
             )
285
-
286
-        return memberlist
313
+        return members
287 314
 
288 315
 
289 316
 class Folder(Workspace):
@@ -295,11 +322,6 @@ class Folder(Workspace):
295 322
         super(Folder, self).__init__(path, environ, workspace)
296 323
 
297 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 326
     def __repr__(self) -> str:
305 327
         return "<DAVCollection: Folder (%s)>" % self.content.label
@@ -313,13 +335,9 @@ class Folder(Workspace):
313 335
     def getLastModified(self) -> float:
314 336
         return mktime(self.content.updated.timetuple())
315 337
 
316
-    def handleDelete(self):
338
+    def delete(self):
317 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 341
     def supportRecursiveMove(self, destpath: str):
324 342
         return True
325 343
 
@@ -377,32 +395,33 @@ class Folder(Workspace):
377 395
         parent = self.provider.get_parent_from_path(
378 396
             normpath(destpath),
379 397
             self.content_api,
380
-            WorkspaceApi(self.environ['user']))
398
+            WorkspaceApi(self.user)
399
+        )
381 400
 
382 401
         workspace = self.provider.get_workspace_from_path(
383 402
             normpath(destpath),
384
-            WorkspaceApi(self.environ['user'])
403
+            WorkspaceApi(self.user)
385 404
         )
386 405
 
387 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 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 410
             else:
402 411
                 try:
403
-                    self.content_api.move_recursively(self.content, parent, parent.workspace)
412
+                    workspace_id = parent.workspace.workspace_id
404 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 426
         transaction.commit()
408 427
 
@@ -416,7 +435,7 @@ class HistoryFolder(Folder):
416 435
         self._is_deleted = type == HistoryType.Deleted
417 436
 
418 437
         self.content_api = ContentApi(
419
-            current_user=UserApi(None).get_one_by_email(environ['http_authenticator.username']),
438
+            current_user=self.user,
420 439
             show_archived=self._is_archived,
421 440
             show_deleted=self._is_deleted
422 441
         )
@@ -447,7 +466,8 @@ class HistoryFolder(Folder):
447 466
     def getMemberNames(self) -> [str]:
448 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 471
             if (self._is_archived and content.is_archived or
452 472
                 self._is_deleted and content.is_deleted or
453 473
                 not (content.is_archived or self._is_archived or content.is_deleted or self._is_deleted))\
@@ -475,23 +495,23 @@ class HistoryFolder(Folder):
475 495
         return True
476 496
 
477 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 511
 class DeletedFolder(HistoryFolder):
487 512
     def __init__(self, path: str, environ: dict, workspace: data.Workspace, content: data.Content=None):
488 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 515
         self._file_count = 0
496 516
 
497 517
     def __repr__(self):
@@ -514,7 +534,7 @@ class DeletedFolder(HistoryFolder):
514 534
         )
515 535
 
516 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 538
             environ=self.environ
519 539
             )
520 540
 
@@ -541,36 +561,40 @@ class DeletedFolder(HistoryFolder):
541 561
         raise DAVError(HTTP_FORBIDDEN)
542 562
 
543 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 580
         if self._file_count > 0 and self.provider.show_history():
552
-            memberlist.append(
581
+            members.append(
553 582
                 HistoryFolder(
554 583
                     path=self.path + '/' + ".history",
555 584
                     environ=self.environ,
556 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 594
 class ArchivedFolder(HistoryFolder):
566 595
     def __init__(self, path: str, environ: dict, workspace: data.Workspace, content: data.Content=None):
567 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 598
         self._file_count = 0
575 599
 
576 600
     def __repr__(self) -> str:
@@ -593,20 +617,19 @@ class ArchivedFolder(HistoryFolder):
593 617
         )
594 618
 
595 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 621
             environ=self.environ
598
-            )
622
+        )
599 623
 
600 624
     def getMemberNames(self) -> [str]:
601 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 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 634
         return retlist
612 635
 
@@ -620,25 +643,34 @@ class ArchivedFolder(HistoryFolder):
620 643
         raise DAVError(HTTP_FORBIDDEN)
621 644
 
622 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 662
         if self._file_count > 0 and self.provider.show_history():
631
-            memberlist.append(
663
+            members.append(
632 664
                 HistoryFolder(
633 665
                     path=self.path + '/' + ".history",
634 666
                     environ=self.environ,
635 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 676
 class HistoryFileFolder(HistoryFolder):
@@ -663,28 +695,29 @@ class HistoryFileFolder(HistoryFolder):
663 695
         )
664 696
 
665 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 699
         ret = []
667 700
 
668 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 704
         return ret
674 705
 
675 706
     def getMember(self, item_id) -> DAVCollection:
676 707
 
677 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 712
         if self.content.type == ContentType.File:
680 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 715
                 environ=self.environ,
683
-                content=self.content, 
716
+                content=self.content,
684 717
                 content_revision=revision)
685 718
         else:
686 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 721
                 environ=self.environ,
689 722
                 content=self.content,
690 723
                 content_revision=revision)
@@ -693,14 +726,28 @@ class HistoryFileFolder(HistoryFolder):
693 726
         raise DAVError(HTTP_FORBIDDEN)
694 727
 
695 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 753
 class File(DAVNonCollection):
@@ -708,18 +755,17 @@ class File(DAVNonCollection):
708 755
         super(File, self).__init__(path, environ)
709 756
 
710 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 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 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 770
     def __repr__(self) -> str:
725 771
         return "<DAVNonCollection: File (%d)>" % self.content.revision_id
@@ -734,8 +780,7 @@ class File(DAVNonCollection):
734 780
         return mktime(self.content.created.timetuple())
735 781
 
736 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 785
     def getLastModified(self) -> float:
741 786
         return mktime(self.content.updated.timetuple())
@@ -755,10 +800,6 @@ class File(DAVNonCollection):
755 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 803
     def moveRecursive(self, destpath):
763 804
         """As we support recursive move, copymovesingle won't be called, though with copy it'll be called
764 805
             but i have to check if the client ever call that function..."""
@@ -813,25 +854,25 @@ class File(DAVNonCollection):
813 854
         parent = self.provider.get_parent_from_path(
814 855
             normpath(destpath),
815 856
             self.content_api,
816
-            WorkspaceApi(self.environ['user'])
857
+            WorkspaceApi(self.user)
817 858
         )
818 859
 
819 860
         workspace = self.provider.get_workspace_from_path(
820 861
             normpath(destpath),
821
-            WorkspaceApi(self.environ['user'])
862
+            WorkspaceApi(self.user)
822 863
         )
823 864
 
824 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 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 877
         transaction.commit()
837 878
 
@@ -851,7 +892,8 @@ class HistoryFile(File):
851 892
         return "<DAVNonCollection: HistoryFile (%s-%s)" % (self.content.content_id, self.content.file_name)
852 893
 
853 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 898
     def getContent(self):
857 899
         filestream = compat.BytesIO()
@@ -893,11 +935,17 @@ class OtherFile(File):
893 935
 
894 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 944
     def getDisplayName(self) -> str:
897 945
         return self.content.get_label()
898 946
 
899 947
     def getPreferredPath(self):
900
-        return self.path + '.html'
948
+        return self.path
901 949
 
902 950
     def __repr__(self) -> str:
903 951
         return "<DAVNonCollection: OtherFile (%s)" % self.content.file_name
@@ -917,7 +965,7 @@ class OtherFile(File):
917 965
 
918 966
     def design(self):
919 967
         if self.content.type == ContentType.Page:
920
-            return designPage(self.content, self.content.revision)
968
+            return designPage(self.content, self.content_revision)
921 969
         else:
922 970
             return designThread(
923 971
                 self.content,
@@ -930,12 +978,14 @@ class HistoryOtherFile(OtherFile):
930 978
     def __init__(self, path: str, environ: dict, content: data.Content, content_revision: data.ContentRevisionRO):
931 979
         super(HistoryOtherFile, self).__init__(path, environ, content)
932 980
         self.content_revision = content_revision
981
+        self.content_designed = self.design()
933 982
 
934 983
     def __repr__(self) -> str:
935 984
         return "<DAVNonCollection: HistoryOtherFile (%s-%s)" % (self.content.file_name, self.content.id)
936 985
 
937 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 990
     def getContent(self):
941 991
         filestream = compat.BytesIO()
@@ -963,10 +1013,3 @@ class HistoryOtherFile(OtherFile):
963 1013
     def copyMoveSingle(self, destpath, ismove):
964 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 Ver fichero

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

+ 13 - 0
tracim/tracim/model/data.py Ver fichero

@@ -533,6 +533,7 @@ class ContentRevisionRO(DeclarativeBase):
533 533
     updated = Column(DateTime, unique=False, nullable=False, default=datetime.utcnow)
534 534
     is_deleted = Column(Boolean, unique=False, nullable=False, default=False)
535 535
     is_archived = Column(Boolean, unique=False, nullable=False, default=False)
536
+    is_temporary = Column(Boolean, unique=False, nullable=False, default=False)
536 537
     revision_type = Column(Unicode(32), unique=False, nullable=False, default='')
537 538
 
538 539
     workspace_id = Column(Integer, ForeignKey('workspaces.workspace_id'), unique=False, nullable=True)
@@ -852,6 +853,18 @@ class Content(DeclarativeBase):
852 853
         return ContentRevisionRO.is_archived
853 854
 
854 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 868
     def revision_type(self) -> str:
856 869
         return self.revision.revision_type
857 870