Browse Source

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

Nonolost 8 years ago
parent
commit
bb447d7988

+ 8 - 2
tracim/migration/versions/b4b8d57b54e5_add_hash_column_for_digest_.py View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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 View File

@@ -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