Browse Source

correction of some behavior and working on new deletion/archiving behavior

Nonolost 8 years ago
parent
commit
cef2bfa39d

+ 3 - 5
tracim/tracim/lib/content.py View File

@@ -364,7 +364,7 @@ class ContentApi(object):
364 364
 
365 365
         return self._base_query(workspace).filter(Content.content_id==content_id).filter(Content.type==content_type).one()
366 366
 
367
-    def get_one_revision(self, revision_id: int = None) -> Content:
367
+    def get_one_revision(self, revision_id: int = None) -> ContentRevisionRO:
368 368
         """
369 369
         This method allow us to get directly any revision with its id
370 370
         :param revision_id: The content's revision's id that we want to return
@@ -372,11 +372,9 @@ class ContentApi(object):
372 372
         """
373 373
         assert revision_id is not None# DYN_REMOVE
374 374
 
375
-        revision = DBSession.query(ContentRevisionRO).filter(ContentRevisionRO.revision_id == revision_id)
376
-        
377
-        result = self._base_query(None)
375
+        revision = DBSession.query(ContentRevisionRO).filter(ContentRevisionRO.revision_id == revision_id).one()
378 376
 
379
-        return result.filter(Content.revision_id == revision_id).one()
377
+        return revision
380 378
 
381 379
     def get_one_by_label_and_parent(self, content_label: str, content_parent: Content = None,
382 380
                                     workspace: Workspace = None) -> Content:

+ 3 - 2
tracim/tracim/lib/webdav/__init__.py View File

@@ -20,8 +20,9 @@ class FileStream(object):
20 20
         self._file_name = file_name if file_name != '' else self._content.file_name
21 21
         self._content = content
22 22
         self._api = content_api
23
+        self._is_new_file = new_file
23 24
 
24
-    def beginWrite(self, contentType) -> FileStream:
25
+    def beginWrite(self, contentType) -> 'FileStream':
25 26
         return self
26 27
 
27 28
     def endWrite(self, withErrors: bool):
@@ -36,7 +37,7 @@ class FileStream(object):
36 37
         for part in self._buffer:
37 38
             item_content += part
38 39
 
39
-        if new_file:
40
+        if self._is_new_file:
40 41
             file = self._api.create(
41 42
                 content_type=ContentType.File,
42 43
                 workspace=self._content.workspace,

+ 277 - 0
tracim/tracim/lib/webdav/design.py View File

@@ -0,0 +1,277 @@
1
+from tracim.model.data import VirtualEvent
2
+from tracim.model import data
3
+from tracim.model.data import ContentType
4
+from datetime import datetime
5
+
6
+def create_readable_date(created, delta_from_datetime: datetime = None):
7
+    aff = ''
8
+
9
+    if not delta_from_datetime:
10
+        delta_from_datetime = datetime.now()
11
+
12
+    delta = delta_from_datetime - created
13
+
14
+    if delta.days > 0:
15
+        if delta.days >= 365:
16
+            aff = '%d year%s ago' % (delta.days / 365, 's' if delta.days / 365 >= 2 else '')
17
+        elif delta.days >= 30:
18
+            aff = '%d month%s ago' % (delta.days / 30, 's' if delta.days / 30 >= 2 else '')
19
+        else:
20
+            aff = '%d day%s ago' % (delta.days, 's' if delta.days >= 2 else '')
21
+    else:
22
+        if delta.seconds < 60:
23
+            aff = '%d second%s ago' % (delta.seconds, 's' if delta.seconds > 1 else '')
24
+        elif delta.seconds / 60 < 60:
25
+            aff = '%d minute%s ago' % (delta.seconds / 60, 's' if delta.seconds / 60 >= 2 else '')
26
+        else:
27
+            aff = '%d hour%s ago' % (delta.seconds / 3600, 's' if delta.seconds / 3600 >= 2 else '')
28
+
29
+    return aff
30
+
31
+def designPage(content: data.Content, content_revision: data.ContentRevisionRO) -> str:
32
+    # f = open('wsgidav/addons/webdav/style.css', 'r')
33
+    style = ''  # f.read()
34
+    # f.close()
35
+
36
+    hist = content.get_history()
37
+    histHTML = '<table class="table table-striped table-hover">'
38
+    for event in hist:
39
+        if isinstance(event, VirtualEvent):
40
+            date = event.create_readable_date()
41
+            _LABELS = {
42
+                'archiving': 'Item archived',
43
+                'content-comment': 'Item commented',
44
+                'creation': 'Item created',
45
+                'deletion': 'Item deleted',
46
+                'edition': 'item modified',
47
+                'revision': 'New revision',
48
+                'status-update': 'New status',
49
+                'unarchiving': 'Item unarchived',
50
+                'undeletion': 'Item undeleted',
51
+                'move': 'Item moved'
52
+            }
53
+
54
+            label = _LABELS[event.type.id]
55
+
56
+            histHTML += '''
57
+                <tr class="%s">
58
+                    <td class="my-align"><span class="label label-default"><i class="fa %s"></i> %s</span></td>
59
+                    <td>%s</td>
60
+                    <td>%s</td>
61
+                    <td>%s</td>
62
+                </tr>
63
+                ''' % ('warning' if event.id == content_revision.revision_id else '',
64
+                       event.type.icon,
65
+                       label,
66
+                       date,
67
+                       event.owner.display_name,
68
+                       '<i class="fa fa-caret-left"></i> shown' if event.id == content_revision.revision_id else '''<span><a class="revision-link" href="/.history/%s/%s-%s">(View revision)</a></span>''' % (
69
+                       content_revision.label, event.id, event.ref_object.label) if event.type.id in ['revision', 'creation', 'edition'] else '')
70
+
71
+    histHTML += '</table>'
72
+
73
+    file = '''
74
+<html>
75
+<head>
76
+	<title>%s</title>
77
+	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
78
+	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
79
+	<link rel="stylesheet" href="/home/arnaud/Documents/css/style.css">
80
+	<script type="text/javascript" src="/home/arnaud/Documents/css/script.js"></script>
81
+</head>
82
+<body>
83
+    <div id="left" class="col-lg-8 col-md-12 col-sm-12 col-xs-12">
84
+        <div class="title page">
85
+            <div class="title-text">
86
+                <i class="fa fa-file-text-o title-icon page"></i>
87
+                <h1>%s</h1>
88
+                <h6>page created on <b>%s</b> by <b>%s</b></h6>
89
+            </div>
90
+            <div class="pull-right">
91
+                <div class="btn-group btn-group-vertical">
92
+                    <a class="btn btn-default">
93
+                        <i class="fa fa-external-link"></i> View in tracim</a>
94
+                    </a>
95
+                </div>
96
+            </div>
97
+        </div>
98
+        <div class="content col-xs-12 col-sm-12 col-md-12 col-lg-12">
99
+            %s
100
+        </div>
101
+    </div>
102
+    <div id="right" class="col-lg-4 col-md-12 col-sm-12 col-xs-12">
103
+        <h4>History</h4>
104
+        %s
105
+    </div>
106
+    <script type="text/javascript">
107
+        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
+            }
114
+        }
115
+    </script>
116
+</body>
117
+</html>
118
+        ''' % (content_revision.label,
119
+               content_revision.label,
120
+               content.created.strftime("%B %d, %Y at %H:%m"),
121
+               content.owner.display_name,
122
+               content_revision.description,
123
+               histHTML)
124
+
125
+    return file
126
+
127
+def designThread(content: data.Content, content_revision: data.ContentRevisionRO, comments) -> str:
128
+
129
+        hist = content.get_history()
130
+
131
+        allT = []
132
+        allT += comments
133
+        allT += hist
134
+        allT.sort(key=lambda x: x.created, reverse=True)
135
+
136
+        disc = ''
137
+        participants = {}
138
+        for t in allT:
139
+            if t.type == ContentType.Comment:
140
+                disc += '''
141
+                    <div class="row comment comment-row">
142
+                        <i class="fa fa-comment-o comment-icon"></i>
143
+                            <div class="comment-content">
144
+                            <h5>
145
+                                <span class="comment-author"><b>%s</b> wrote :</span>
146
+                                <div class="pull-right text-right">%s</div>
147
+                            </h5>
148
+                            %s
149
+                        </div>
150
+                    </div>
151
+                    ''' % (t.owner.display_name, create_readable_date(t.created), t.description)
152
+
153
+                if t.owner.display_name not in participants:
154
+                    participants[t.owner.display_name] = [1, t.created]
155
+                else:
156
+                    participants[t.owner.display_name][0] += 1
157
+            else:
158
+                if isinstance(t, VirtualEvent) and t.type.id != 'comment':
159
+                    _LABELS = {
160
+                        'archiving': 'Item archived',
161
+                        'content-comment': 'Item commented',
162
+                        'creation': 'Item created',
163
+                        'deletion': 'Item deleted',
164
+                        'edition': 'item modified',
165
+                        'revision': 'New revision',
166
+                        'status-update': 'New status',
167
+                        'unarchiving': 'Item unarchived',
168
+                        'undeletion': 'Item undeleted',
169
+                        'move': 'Item moved',
170
+                        'comment' : 'hmmm'
171
+                    }
172
+
173
+                    label = _LABELS[t.type.id]
174
+
175
+                    disc += '''
176
+                    <div class="row comment comment-row to-hide">
177
+                        <i class="fa %s comment-icon"></i>
178
+                            <div class="comment-content">
179
+                            <h5>
180
+                                <span class="comment-author"><b>%s</b></span>
181
+                                <div class="pull-right text-right">%s</div>
182
+                            </h5>
183
+                            %s %s
184
+                        </div>
185
+                    </div>
186
+                    ''' % (t.type.icon,
187
+                           t.owner.display_name,
188
+                           t.create_readable_date(),
189
+                           label,
190
+                           '''<span><a class="revision-link" href="/.history/%s/%s-%s">(View revision)</a></span>''' % (
191
+                               content_revision.label,
192
+                               t.id,
193
+                               t.ref_object.label) if t.type.id in ['revision', 'creation', 'edition'] else '')
194
+
195
+        page = '''
196
+<html>
197
+<head>
198
+	<title>%s</title>
199
+	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
200
+	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
201
+	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
202
+	<link rel="stylesheet" href="/home/arnaud/Documents/css/style.css">
203
+	<script type="text/javascript" src="/home/arnaud/Documents/css/script.js"></script>
204
+</head>
205
+<body>
206
+    <div id="left" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
207
+        <div class="title thread">
208
+            <div class="title-text">
209
+                <i class="fa fa-comments-o title-icon thread"></i>
210
+                <h1>%s</h1>
211
+                <h6>thread created on <b>%s</b> by <b>%s</b></h6>
212
+            </div>
213
+            <div class="pull-right">
214
+                <div class="btn-group btn-group-vertical">
215
+                    <a class="btn btn-default" onclick="hide_elements()">
216
+                        <i id="hideshow" class="fa fa-eye-slash"></i> <span id="hideshowtxt" >Hide history</span></a>
217
+                    </a>
218
+                    <a class="btn btn-default">
219
+                        <i class="fa fa-external-link"></i> View in tracim</a>
220
+                    </a>
221
+                </div>
222
+            </div>
223
+        </div>
224
+        <div class="content col-xs-12 col-sm-12 col-md-12 col-lg-12">
225
+            <div class="description">
226
+                <span class="description-text">%s</span>
227
+            </div>
228
+            %s
229
+        </div>
230
+    </div>
231
+    <script type="text/javascript">
232
+        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
+        }
240
+
241
+        function hide_elements() {
242
+            elems = document.getElementsByClassName('to-hide');
243
+            if (elems.length > 0) {
244
+                for(var i = 0; i < elems.length; i++) {
245
+                    $(elems[i]).addClass('to-show')
246
+                    $(elems[i]).hide();
247
+                }
248
+                while (elems.length>0) {
249
+                    $(elems[0]).removeClass('to-hide');
250
+                }
251
+                $('#hideshow').addClass('fa-eye').removeClass('fa-eye-slash');
252
+                $('#hideshowtxt').html('Show history');
253
+            }
254
+            else {
255
+                elems = document.getElementsByClassName('to-show');
256
+                for(var i = 0; i<elems.length; i++) {
257
+                    $(elems[i]).addClass('to-hide');
258
+                    $(elems[i]).show();
259
+                }
260
+                while (elems.length>0) {
261
+                    $(elems[0]).removeClass('to-show');
262
+                }
263
+                $('#hideshow').removeClass('fa-eye').addClass('fa-eye-slash');
264
+                $('#hideshowtxt').html('Hide history');
265
+            }
266
+        }
267
+    </script>
268
+</body>
269
+</html>
270
+        ''' % (content_revision.label,
271
+               content_revision.label,
272
+               content.created.strftime("%B %d, %Y at %H:%m"),
273
+               content.owner.display_name,
274
+               content_revision.description,
275
+               disc)
276
+
277
+        return page

+ 1 - 2
tracim/tracim/lib/webdav/lock_storage.py View File

@@ -1,7 +1,6 @@
1 1
 import time
2 2
 
3 3
 from tracim.lib.webdav.sql_model import Lock, Url2Token
4
-from tracim.lib.webdav import Session
5 4
 from wsgidav import util
6 5
 from wsgidav.lock_manager import normalizeLockRoot, lockString, generateLockToken, validateLock
7 6
 from wsgidav.rw_lock import ReadWriteLock
@@ -42,7 +41,7 @@ class LockStorage(object):
42 41
     LOCK_TIME_OUT_MAX = 4 * 604800  # 1 month, in seconds
43 42
 
44 43
     def __init__(self):
45
-        self._session = Session()
44
+        self._session = None# todo Session()
46 45
         self._lock = ReadWriteLock()
47 46
 
48 47
     def __repr__(self):

+ 167 - 58
tracim/tracim/lib/webdav/sql_dav_provider.py View File

@@ -14,6 +14,7 @@ from wsgidav.dav_provider import DAVProvider
14 14
 from wsgidav.lock_manager import LockManager
15 15
 from tracim.model.data import ContentType
16 16
 
17
+from tracim.lib.content import ContentRevisionRO
17 18
 ######################################
18 19
 
19 20
 __docformat__ = "reStructuredText"
@@ -39,52 +40,83 @@ class Provider(DAVProvider):
39 40
         if manage_lock:
40 41
             self.lockManager = LockManager(LockStorage())
41 42
 
43
+        self._show_archive = True
44
+        self._show_delete = True
45
+        self._show_history = True
46
+
47
+    def show_history(self):
48
+        return self._show_history
49
+
50
+    def show_delete(self):
51
+        return self._show_delete
52
+
53
+    def show_archive(self):
54
+        return self._show_archive
55
+
42 56
     def __repr__(self):
43 57
         return 'Provider'
44 58
 
45 59
     #########################################################
46 60
     # Everything override from DAVProvider
47 61
     def getResourceInst(self, path, environ):
62
+        #if not self.exists(path, environ):
63
+        #    return None
48 64
 
49
-        working_path = path
50
-
51
-        if not self.exists(path, environ):
52
-            return None
53
-
54
-        if path == "/":
55
-            return sql_resources.Root(path, environ)
56
-
57
-        norm_path = normpath(working_path)
65
+        norm_path = normpath(path)
66
+        root_path = environ['http_authenticator.realm']
67
+        parent_path = dirname(norm_path)
58 68
 
59 69
         workspace_api = WorkspaceApi(environ['user'])
70
+        content_api = ContentApi(
71
+            environ['user'],
72
+            show_archived=self._show_archive,
73
+            show_deleted=self._show_delete
74
+        )
60 75
 
61
-        if dirname(norm_path) == "/":
62
-            workspace = self.get_workspace_from_path(norm_path, workspace_api)
63
-            return sql_resources.Workspace(path, environ, workspace)
64
-
65
-
66
-        api = ContentApi(environ['user'], show_archived=True, show_deleted=True)
67
-
68
-        working_path = self.reduce_path(path)
76
+        # case we're requesting the root racine of webdav
77
+        if path == root_path:
78
+            return sql_resources.Root(path, environ)
79
+        # case we're at the root racine of a workspace
80
+        elif parent_path == root_path:
81
+            return sql_resources.Workspace(
82
+                path=norm_path,
83
+                environ=environ,
84
+                workspace=self.get_workspace_from_path(
85
+                    norm_path,
86
+                    workspace_api
87
+                )
88
+            )
69 89
 
70
-        content = self.get_content_from_path(working_path, api, workspace_api)
90
+        content = self.get_content_from_path(
91
+            path=norm_path,
92
+            content_api=content_api,
93
+            workspace_api=workspace_api
94
+        )
71 95
 
72 96
         # is archive
73 97
         is_archived_folder = re.search(r'/\.archived$', norm_path) is not None
74 98
 
75
-        if is_archived_folder:
76
-            return sql_resources.ArchivedFolder(path, environ, content)
99
+        if self._show_archive and is_archived_folder:
100
+            return sql_resources.ArchivedFolder(
101
+                path=norm_path,
102
+                environ=environ,
103
+                content=content,
104
+            )
77 105
 
78 106
         # is delete
79 107
         is_deleted_folder = re.search(r'/\.deleted$', norm_path) is not None
80 108
 
81
-        if is_deleted_folder:
82
-            return sql_resources.DeletedFolder(path, environ, content)
109
+        if self._show_delete and is_deleted_folder:
110
+            return sql_resources.DeletedFolder(
111
+                path=norm_path,
112
+                environ=environ,
113
+                content=content
114
+            )
83 115
 
84 116
         # is history
85 117
         is_history_folder = re.search(r'/\.history$', norm_path) is not None
86 118
 
87
-        if is_history_folder:
119
+        if self._show_history and is_history_folder:
88 120
             is_deleted_folder = re.search(r'/\.deleted/\.history$', norm_path) is not None
89 121
             is_archived_folder = re.search(r'/\.archived/\.history$', norm_path) is not None
90 122
 
@@ -92,20 +124,29 @@ class Provider(DAVProvider):
92 124
                 else HistoryType.Archived if is_archived_folder \
93 125
                 else HistoryType.Standard
94 126
 
95
-            return sql_resources.HistoryFolder(path, environ, content, type)
127
+            return sql_resources.HistoryFolder(
128
+                path=norm_path,
129
+                environ=environ,
130
+                content=content,
131
+                type=type
132
+            )
96 133
 
97 134
         # is history
98 135
         is_history_file_folder = re.search(r'/\.history/([^/]+)$', norm_path) is not None
99 136
 
100
-        if is_history_file_folder:
101
-            return sql_resources.HistoryFileFolder(path, environ, content)
137
+        if self._show_history and is_history_file_folder:
138
+            return sql_resources.HistoryFileFolder(
139
+                path=norm_path,
140
+                environ=environ,
141
+                content=content
142
+            )
102 143
 
103 144
         # is history
104 145
         is_history_file = re.search(r'/\.history/[^/]+/(\d+)-.+', norm_path) is not None
105 146
 
106
-        if is_history_file:
107
-            content = api.get_one_revision2(re.search(r'/\.history/[^/]+/(\d+)-.+', norm_path).group(1))
108
-            content_revision = self.get_content_from_revision(re.search(r'/\.history/[^/]+/(\d+)-.+', norm_path).group(1), api)
147
+        if self._show_history and is_history_file:
148
+            content_revision = content_api.get_one_revision(re.search(r'/\.history/[^/]+/(\d+)-.+', norm_path).group(1))
149
+            content = self.get_content_from_revision(content_revision, content_api)
109 150
 
110 151
             if content.type == ContentType.File:
111 152
                 return sql_resources.HistoryFile(path, environ, content, content_revision)
@@ -115,30 +156,36 @@ class Provider(DAVProvider):
115 156
         # other
116 157
         if content.type == ContentType.Folder:
117 158
             return sql_resources.Folder(path, environ, content)
118
-
119
-        if content.type == ContentType.File:
159
+        elif content.type == ContentType.File:
120 160
             return sql_resources.File(path, environ, content, False)
121
-        else:
161
+        elif content.type in [ContentType.Page, ContentType.Thread]:
122 162
             return sql_resources.OtherFile(path, environ, content)
163
+        else:
164
+            return None
123 165
 
124 166
     def exists(self, path, environ):
125
-        path = normpath(path)
126
-        if path == "/":
127
-            return True
128
-        elif dirname(path) == "/":
129
-            return self.get_workspace_from_path(
130
-                path,
131
-                WorkspaceApi(environ['user'])
132
-            ) is not None
167
+        norm_path = normpath(path)
168
+        parent_path = dirname(norm_path)
169
+        root_path = environ['http_authenticator.realm']
133 170
 
134
-        api = ContentApi(
171
+        workspace_api = WorkspaceApi(environ['user'])
172
+        content_api = ContentApi(
135 173
             current_user=environ['user'],
136 174
             show_archived=True,
137 175
             show_deleted=True
138 176
         )
139
-        wapi = WorkspaceApi(environ['user'])
140 177
 
141
-        norm_path = normpath(path)
178
+        if path == root_path:
179
+            return sql_resources.Root(path, environ)
180
+        elif parent_path == root_path:
181
+            return sql_resources.Workspace(
182
+                path=norm_path,
183
+                environ=environ,
184
+                workspace=self.get_workspace_from_path(
185
+                    norm_path,
186
+                    workspace_api
187
+                )
188
+            ) is not None
142 189
 
143 190
         is_archived = re.search(r'/\.archived/(\.history/)?(?!\.history)[^/]*(/\.)?(history|deleted|archived)?$', norm_path) is not None
144 191
 
@@ -148,16 +195,13 @@ class Provider(DAVProvider):
148 195
 
149 196
         if revision_id:
150 197
             revision_id = revision_id.group(1)
151
-            return self.get_content_from_revision(revision_id, api) is not None
152
-
153
-        working_path = self.reduce_path(path)
154
-
155
-        content = self.get_content_from_path(working_path, api, wapi)
198
+            content = content_api.get_one_revision(revision_id)
199
+        else:
200
+            content = self.get_content_from_path(norm_path, content_api, workspace_api)
156 201
 
157 202
         return content is not None \
158 203
             and content.is_deleted == is_deleted \
159
-            and content.is_archived == is_archived \
160
-            and (revision_id is None or content.revision_id == revision_id)
204
+            and content.is_archived == is_archived
161 205
 
162 206
     def reduce_path(self, path):
163 207
         path = re.sub(r'/\.archived', r'', path)
@@ -168,20 +212,30 @@ class Provider(DAVProvider):
168 212
 
169 213
         return path
170 214
 
171
-    def get_content_from_path(self, path, api: ContentApi, workspace_api: WorkspaceApi):
172
-        path = normpath(path)
215
+    def get_content_from_path(self, path, content_api: ContentApi, workspace_api: WorkspaceApi):
216
+        path = self.reduce_path(path)
217
+
173 218
         workspace = self.get_workspace_from_path(path, workspace_api)
174 219
 
175 220
         if basename(dirname(path)) == workspace.label:
176
-            return api.get_one_by_label_and_parent(basename(path), workspace=workspace)
221
+            try:
222
+                return content_api.get_one_by_label_and_parent(basename(path), workspace=workspace)
223
+            except:
224
+                return None
177 225
         else:
178
-            parent = self.get_parent_from_path(path, api, workspace_api)
226
+            parent = self.get_parent_from_path(path, content_api, workspace_api)
179 227
             if parent is not None:
180
-                return api.get_one_by_label_and_parent(basename(path), content_parent=parent)
228
+                try:
229
+                    return content_api.get_one_by_label_and_parent(basename(path), content_parent=parent)
230
+                except:
231
+                    return None
181 232
             return None
182 233
 
183
-    def get_content_from_revision(self, revision_id: int, api:ContentApi):
184
-        return api.get_one_revision(revision_id)
234
+    def get_content_from_revision(self, revision: ContentRevisionRO, api:ContentApi):
235
+        try:
236
+            return api.get_one(revision.content_id, ContentType.Any)
237
+        except:
238
+            return None
185 239
 
186 240
     def get_parent_from_path(self, path, api: ContentApi, workspace_api: WorkspaceApi):
187 241
         return self.get_content_from_path(dirname(path), api, workspace_api)
@@ -189,7 +243,11 @@ class Provider(DAVProvider):
189 243
     def get_workspace_from_path(self, path: str, api: WorkspaceApi):
190 244
         assert path.startswith('/')
191 245
 
192
-        return api.get_one_by_label(path.split('/')[1])
246
+        print(path)
247
+        try:
248
+            return api.get_one_by_label(path.split('/')[1])
249
+        except:
250
+            return None
193 251
 
194 252
     #########################################################
195 253
     # Everything that transform path
@@ -494,3 +552,54 @@ class Provider(DAVProvider):
494 552
         last_item.child_revision_id = new_item.id
495 553
 
496 554
         self.session.commit()'''
555
+
556
+"""
557
+
558
+{'wsgidav.dump_request_body': False,
559
+ 'wsgi.run_once': False,
560
+ 'wsgi.multiprocess': False,
561
+ 'wsgi.multithread': True,
562
+ 'QUERY_STRING': '',
563
+ 'REQUEST_URI': b'/nouveau/',
564
+ 'wsgidav.dump_response_body': False,
565
+ 'SERVER_PROTOCOL': 'HTTP/1.1',
566
+ 'REMOTE_ADDR': '127.0.0.1',
567
+ 'wsgidav.verbose': 1,
568
+ 'wsgi.version': (1, 0),
569
+ 'wsgidav.config': {
570
+     'middleware_stack':[],
571
+                       'propsmanager': None,
572
+                       'add_header_MS_Author_Via': True,
573
+                       'acceptbasic': True,
574
+                       'user_mapping': {},
575
+                       'enable_loggers': [],
576
+                       'locksmanager': True,
577
+                       'mount_path': None,
578
+                       'catchall': False,
579
+                       'unquote_path_info': False,
580
+                       'provider_mapping': {'': Provider},
581
+                       'port': 3030,
582
+                       'Provider': [],
583
+                       'verbose': 1,
584
+                       'SQLDomainController': [],
585
+                       'domaincontroller': [],
586
+     'acceptdigest': True,
587
+     'dir_browser': {
588
+         'ms_sharepoint_urls': False,
589
+         'ms_mount': False,
590
+         'davmount': False,
591
+         'enable': True,
592
+         'ms_sharepoint_plugin': True,
593
+         'response_trailer': ''
594
+     },
595
+     'defaultdigest': True,
596
+     'host': '0.0.0.0',
597
+     'ext_servers': ['cherrypy-bundled', 'wsgidav']
598
+ },
599
+ 'http_authenticator.realm': '/',
600
+ 'HTTP_AUTHORIZATION': 'Digest username="admin@admin.admin", realm="/", nonce="=", uri="/nouveau/", algorithm=MD5, response="9c78c484263409b3385ead95ea7bf65b", '
601
+                       'cnonce="MHgyMzNkZjkwOjQ4OTU6MTQ2OTc3OTI1NQ==", nc=00000471, qop=auth',
602
+ 'HTTP_ACCEPT_ENCODING': 'gzip, deflate',
603
+ 'HTTP_USER_AGENT': 'gvfs/1.22.2', 'wsgidav.debug_break': False,
604
+ 'HTTP_CONNECTION': 'Keep-Alive', 'SERVER_PORT': '3030', 'CONTENT_LENGTH': '235', 'HTTP_HOST': '127.0.0.1:3030', 'REQUEST_METHOD': 'PROPFIND', 'HTTP_APPLY_TO_REDIRECT_REF': 'T', 'SERVER_NAME': 'WsgiDAV/3.0.0pre1 CherryPy/3.2.4 Python/3.4.2', 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'wsgi.url_scheme': 'http', 'user': <User: email='admin@admin.admin', display='Global manager'>, 'HTTP_ACCEPT_LANGUAGE': 'en-us, en;q=0.9', 'ACTUAL_SERVER_PROTOCOL': 'HTTP/1.1', 'REMOTE_PORT': '48375', 'CONTENT_TYPE': 'application/xml', 'SCRIPT_NAME': '', 'wsgi.input': <wsgidav.server.cherrypy.wsgiserver.wsgiserver3.KnownLengthRFile object at 0x7fbc8410ce48>, 'wsgidav.username': 'admin@admin.admin', 'http_authenticator.username': 'admin@admin.admin', 'wsgidav.provider': Provider, 'PATH_INFO': '/nouveau/', 'HTTP_DEPTH': '1', 'SERVER_SOFTWARE': 'WsgiDAV/3.0.0pre1 CherryPy/3.2.4 Python/3.4.2 Server'}
605
+"""

+ 7 - 7
tracim/tracim/lib/webdav/sql_model.py View File

@@ -5,13 +5,13 @@ from sqlalchemy import ForeignKey
5 5
 from sqlalchemy import Sequence
6 6
 from sqlalchemy.orm import deferred
7 7
 from sqlalchemy.types import DateTime, Integer, LargeBinary, Unicode, UnicodeText, Float
8
-from tracim.lib.webdav import Base
8
+
9 9
 from wsgidav.compat import to_unicode, to_bytes
10 10
 
11 11
 # ==================================================
12 12
 # Content
13 13
 # ==================================================
14
-class Workspace(Base):
14
+class Workspace(object):
15 15
     __tablename__ = 'my_workspaces'
16 16
 
17 17
     workspace_id = Column(Integer, Sequence('my_seq__workspace__id'), autoincrement=True, primary_key=True)
@@ -24,7 +24,7 @@ class Workspace(Base):
24 24
         return "<Workspace %s : %s>" % (self.workspace_id, self.label)
25 25
 
26 26
 
27
-class User(Base):
27
+class User(object):
28 28
     __tablename__ = 'my_users'
29 29
 
30 30
     user_id = Column(Integer, Sequence('my_seq__users__id'), autoincrement=True, primary_key=True)
@@ -35,7 +35,7 @@ class User(Base):
35 35
         return "<User %s : %s>" % (self.user_id, self.display_name)
36 36
 
37 37
 
38
-class UserWorkspace(Base):
38
+class UserWorkspace(object):
39 39
     __tablename__ = 'my_user_workspace'
40 40
 
41 41
     workspace_id = Column(Integer, ForeignKey('my_workspaces.workspace_id', ondelete="CASCADE"), nullable=False, primary_key=True)
@@ -46,7 +46,7 @@ class UserWorkspace(Base):
46 46
         return "<Role (W:%s, U:%s) : %s" % (self.workspace_id, self.user_id, self.role)
47 47
 
48 48
 
49
-class ItemRevision(Base):
49
+class ItemRevision(object):
50 50
     __tablename__ = 'my_items_revisions'
51 51
 
52 52
     id = Column(Integer, Sequence('my_seq__items__id'), autoincrement=True, primary_key=True)
@@ -67,7 +67,7 @@ class ItemRevision(Base):
67 67
         return "<Content %s : %s in %s>" % (self.id, self.item_name, self.parent_id)
68 68
 
69 69
 
70
-class Lock(Base):
70
+class Lock(object):
71 71
     __tablename__ = 'my_locks'
72 72
 
73 73
     token = Column(UnicodeText, primary_key=True, unique=True, nullable=False)
@@ -81,7 +81,7 @@ class Lock(Base):
81 81
     timeout = Column(Float, unique=False, nullable=False)
82 82
 
83 83
 
84
-class Url2Token(Base):
84
+class Url2Token(object):
85 85
     __tablename__ = 'my_url2token'
86 86
 
87 87
     token = Column(UnicodeText, primary_key=True, unique=True, nullable=False)

+ 97 - 273
tracim/tracim/lib/webdav/sql_resources.py View File

@@ -15,16 +15,11 @@ from wsgidav.compat import PY3
15 15
 from wsgidav.dav_error import DAVError, HTTP_FORBIDDEN
16 16
 from wsgidav.dav_provider import DAVCollection, DAVNonCollection
17 17
 from tracim.model import data, new_revision
18
-from tracim.model.data import Content, ActionDescription, VirtualEvent
18
+from tracim.model.data import Content, ActionDescription
19 19
 from tracim.model.data import ContentType
20
-from wsgidav.dav_provider import DAVResource
20
+from wsgidav.dav_provider import _DAVResource
21 21
 
22
-_CONTENTS = {
23
-    ContentType.Folder: Folder,
24
-    ContentType.File: File,
25
-    ContentType.Page: OtherFile,
26
-    ContentType.Thread: OtherFile
27
-}
22
+from tracim.lib.webdav.design import designThread, designPage
28 23
 
29 24
 class Encapsuler(object):
30 25
     def __init__(self, type: str, api: ContentApi, content: Content):
@@ -40,13 +35,32 @@ class Encapsuler(object):
40 35
 
41 36
         self._type = type
42 37
 
38
+        self._new_name = self.make_name()
39
+
43 40
     def action(self):
44 41
         with new_revision(self._content):
45 42
             self._actions[self._type](self._content)
43
+
44
+            if self._content.label == '':
45
+                self._content.file_name = self._new_name
46
+            else:
47
+                self._content.label = self._new_name
48
+
46 49
             self._api.save(self._content, self._type)
47 50
 
48 51
         transaction.commit()
49 52
 
53
+    def make_name(self):
54
+        new_name = self._content.get_label()
55
+        is_file_name = self._content.label == ''
56
+        add = ''
57
+
58
+        if self._type in [ActionDescription.ARCHIVING, ActionDescription.DELETION]:
59
+            new_name += ' - %s the %s' % (self._type, datetime.now())
60
+        else:
61
+            pass
62
+
63
+        return new_name
50 64
 
51 65
 class Root(DAVCollection):
52 66
     def __init__(self, path: str, environ: dict):
@@ -69,7 +83,7 @@ class Root(DAVCollection):
69 83
     def getMemberNames(self) -> [str]:
70 84
         return [workspace.label for workspace in self._workspace_api.get_all()]
71 85
 
72
-    def getMember(self, label: str) -> Workspace:
86
+    def getMember(self, label: str) -> _DAVResource:
73 87
 
74 88
         workspace = self._workspace_api.get_one_by_label(label)
75 89
 
@@ -122,7 +136,7 @@ class Workspace(DAVCollection):
122 136
 
123 137
         return retlist
124 138
 
125
-    def getMember(self, content_label: str) -> DAVResource:
139
+    def getMember(self, content_label: str) -> _DAVResource:
126 140
 
127 141
         content = self._content_api.get_one_by_label_and_parent(
128 142
             content_label=content_label,
@@ -130,7 +144,7 @@ class Workspace(DAVCollection):
130 144
         )
131 145
 
132 146
         return _CONTENTS[content.type](
133
-            path=self.path + content.get_label(),
147
+            path=self.path + '/' + content.get_label(),
134 148
             environ=self.environ,
135 149
             content=content
136 150
             )
@@ -138,7 +152,7 @@ class Workspace(DAVCollection):
138 152
     def createEmptyResource(self, name: str):
139 153
         raise DAVError(HTTP_FORBIDDEN)
140 154
 
141
-    def createCollection(self, label: str) -> Folder:
155
+    def createCollection(self, label: str) -> 'Folder':
142 156
 
143 157
         folder = self._content_api.create(
144 158
             content_type=ContentType.Folder,
@@ -158,7 +172,7 @@ class Workspace(DAVCollection):
158 172
 
159 173
         transaction.commit()
160 174
 
161
-        return Folder(self.path + label, self.environ, folder)
175
+        return Folder(self.path + '/' + label, self.environ, folder)
162 176
 
163 177
     def delete(self):
164 178
         raise DAVError(HTTP_FORBIDDEN)
@@ -175,7 +189,7 @@ class Workspace(DAVCollection):
175 189
     def setLastModified(self, destpath, timestamp, dryrun):
176 190
         return False
177 191
 
178
-    def getMemberList(self) -> [DAVResource]:
192
+    def getMemberList(self) -> [_DAVResource]:
179 193
         memberlist = []
180 194
 
181 195
         if self.environ['REQUEST_METHOD'] not in ['MOVE', 'COPY', 'DELETE']:
@@ -187,26 +201,26 @@ class Workspace(DAVCollection):
187 201
             if memberlist != [] and self._file_count > 0:
188 202
                 memberlist.append(
189 203
                     HistoryFolder(
190
-                        path=self.path + ".history",
204
+                        path=self.path + '/' + ".history",
191 205
                         environ=self.environ,
192
-                        content=self._content,
206
+                        content=None,
193 207
                         type=HistoryType.Standard
194 208
                         )
195 209
                     )
196 210
 
197 211
             memberlist.append(
198 212
                 DeletedFolder(
199
-                    path=self.path + ".deleted",
213
+                    path=self.path + '/' + ".deleted",
200 214
                     environ=self.environ,
201
-                    content=self._content
215
+                    content=None
202 216
                     )
203 217
                 )
204 218
 
205 219
             memberlist.append(
206 220
                 ArchivedFolder(
207
-                    path=self.path + ".archived",
221
+                    path=self.path + '/' + ".archived",
208 222
                     environ=self.environ,
209
-                    content=self._content
223
+                    content=None
210 224
                     )
211 225
                 )
212 226
 
@@ -245,7 +259,7 @@ class Folder(DAVCollection):
245 259
 
246 260
         return retlist
247 261
 
248
-    def getMember(self, content_label: str) -> DAVResource:
262
+    def getMember(self, content_label: str) -> _DAVResource:
249 263
 
250 264
         content = self._api.get_one_by_label_and_parent(
251 265
             content_label=content_label,
@@ -253,7 +267,7 @@ class Folder(DAVCollection):
253 267
         )
254 268
 
255 269
         return self.provider.getResourceInst(
256
-            path=self.path + content.get_label(),
270
+            path=self.path + '/' + content.get_label(),
257 271
             environ=self.environ
258 272
             )
259 273
 
@@ -265,7 +279,7 @@ class Folder(DAVCollection):
265 279
             new_file=True
266 280
             )
267 281
 
268
-    def createCollection(self, label: str) -> Folder:
282
+    def createCollection(self, label: str) -> _DAVResource:
269 283
 
270 284
         folder = self._api.create(ContentType.Folder, self._content.workspace, self._content, label)
271 285
 
@@ -281,7 +295,7 @@ class Folder(DAVCollection):
281 295
 
282 296
         transaction.commit()
283 297
 
284
-        return Folder(self.path + label, self.environ, folder)
298
+        return Folder(self.path + '/' + label, self.environ, folder)
285 299
 
286 300
     def delete(self):
287 301
         Encapsuler(ActionDescription.DELETION, self._api, self._content).action()
@@ -337,7 +351,7 @@ class Folder(DAVCollection):
337 351
         #self.item.updated = datetime.fromtimestamp(timestamp)
338 352
         #return True
339 353
 
340
-    def getMemberList(self) -> [DAVResource]:
354
+    def getMemberList(self) -> [_DAVResource]:
341 355
         memberlist = []
342 356
 
343 357
         for name in self.getMemberNames():
@@ -349,7 +363,7 @@ class Folder(DAVCollection):
349 363
             if memberlist != [] and self._file_count > 0:
350 364
                 memberlist.append(
351 365
                     HistoryFolder(
352
-                        path=self.path + ".history",
366
+                        path=self.path + '/' + ".history",
353 367
                         environ=self.environ,
354 368
                         content=self._content,
355 369
                         type=HistoryType.Standard
@@ -358,7 +372,7 @@ class Folder(DAVCollection):
358 372
 
359 373
             memberlist.append(
360 374
                 DeletedFolder(
361
-                    path=self.path + ".deleted",
375
+                    path=self.path + '/' + ".deleted",
362 376
                     environ=self.environ,
363 377
                     content=self._content
364 378
                     )
@@ -366,7 +380,7 @@ class Folder(DAVCollection):
366 380
 
367 381
             memberlist.append(
368 382
                 ArchivedFolder(
369
-                    path=self.path + ".archived",
383
+                    path=self.path + '/' + ".archived",
370 384
                     environ=self.environ,
371 385
                     content=self._content
372 386
                     )
@@ -374,7 +388,6 @@ class Folder(DAVCollection):
374 388
 
375 389
         return memberlist
376 390
 
377
-
378 391
 class HistoryFolder(Folder):
379 392
     def __init__(self, path, environ, content: data.Content, type):
380 393
         super(HistoryFolder, self).__init__(path, environ, content)
@@ -399,14 +412,14 @@ class HistoryFolder(Folder):
399 412
     def getLastModified(self) -> float:
400 413
         return mktime(datetime.now().timetuple())
401 414
 
402
-    def getMember(self, content_label: str) -> HistoryFileFolder:
415
+    def getMember(self, content_label: str) -> _DAVResource:
403 416
         content = self._api.get_one_by_label_and_parent(
404 417
             content_label=content_label,
405 418
             content_parent=self._content
406 419
         )
407 420
 
408 421
         return HistoryFileFolder(
409
-            path=self.path + content.get_label(),
422
+            path=self.path + '/' + content.get_label(),
410 423
             environ=self.environ,
411 424
             content=content)
412 425
 
@@ -442,7 +455,7 @@ class HistoryFolder(Folder):
442 455
     def setLastModified(self, destpath: str, timestamp: float, dryrun: bool):
443 456
         return False
444 457
 
445
-    def getMemberList(self) -> [HistoryFileFolder]:
458
+    def getMemberList(self) -> [_DAVResource]:
446 459
         memberlist = []
447 460
         for name in self.getMemberNames():
448 461
             member = self.getMember(name)
@@ -456,7 +469,7 @@ class DeletedFolder(HistoryFolder):
456 469
         super(DeletedFolder, self).__init__(path, environ, content, HistoryType.Deleted)
457 470
 
458 471
         self._api = ContentApi(
459
-            current_user=self._user,
472
+            current_user=environ['user'],
460 473
             show_deleted=True
461 474
         )
462 475
 
@@ -474,7 +487,7 @@ class DeletedFolder(HistoryFolder):
474 487
     def getLastModified(self) -> float:
475 488
         return mktime(datetime.now().timetuple())
476 489
 
477
-    def getMember(self, content_label) -> DAVResource:
490
+    def getMember(self, content_label) -> _DAVResource:
478 491
 
479 492
         content = self._api.get_one_by_label_and_parent(
480 493
             content_label=content_label,
@@ -482,7 +495,7 @@ class DeletedFolder(HistoryFolder):
482 495
         )
483 496
 
484 497
         return self.provider.getResourceInst(
485
-            path=self.path + content.get_label(),
498
+            path=self.path + '/' + content.get_label(),
486 499
             environ=self.environ
487 500
             )
488 501
 
@@ -519,7 +532,7 @@ class DeletedFolder(HistoryFolder):
519 532
     def setLastModified(self, destpath: str, timestamp: float, dryrun: bool):
520 533
         return False
521 534
 
522
-    def getMemberList(self) -> [DAVResource]:
535
+    def getMemberList(self) -> [_DAVResource]:
523 536
         memberlist = []
524 537
 
525 538
         if self.environ['REQUEST_METHOD'] not in ['MOVE', 'COPY', 'DELETE']:
@@ -532,7 +545,7 @@ class DeletedFolder(HistoryFolder):
532 545
             if self._file_count > 0:
533 546
                 memberlist.append(
534 547
                     HistoryFolder(
535
-                        path=self.path + ".history",
548
+                        path=self.path + '/' + ".history",
536 549
                         environ=self.environ,
537 550
                         content=self._content, 
538 551
                         type=HistoryType.Deleted
@@ -565,7 +578,7 @@ class ArchivedFolder(HistoryFolder):
565 578
     def getLastModified(self) -> float:
566 579
         return mktime(datetime.now().timetuple())
567 580
 
568
-    def getMember(self, content_label) -> DAVResource:
581
+    def getMember(self, content_label) -> _DAVResource:
569 582
 
570 583
         content = self._api.get_one_by_label_and_parent(
571 584
             content_label=content_label,
@@ -573,7 +586,7 @@ class ArchivedFolder(HistoryFolder):
573 586
         )
574 587
 
575 588
         return self.provider.getResourceInst(
576
-            path=self.path + content.get_label(),
589
+            path=self.path + '/' + content.get_label(),
577 590
             environ=self.environ
578 591
             )
579 592
 
@@ -610,7 +623,7 @@ class ArchivedFolder(HistoryFolder):
610 623
     def setLastModified(self, destpath: str, timestamp: float, dryrun: bool):
611 624
         return False
612 625
 
613
-    def getMemberList(self) -> [DAVResource]:
626
+    def getMemberList(self) -> [_DAVResource]:
614 627
         memberlist = []
615 628
         
616 629
         if self.environ['REQUEST_METHOD'] not in ['MOVE', 'COPY', 'DELETE']:
@@ -622,7 +635,7 @@ class ArchivedFolder(HistoryFolder):
622 635
             if self._file_count > 0:
623 636
                 memberlist.append(
624 637
                     HistoryFolder(
625
-                        path=self.path + ".history",
638
+                        path=self.path + '/' + ".history",
626 639
                         environ=self.environ,
627 640
                         content=self._content,
628 641
                         type=HistoryType.Archived
@@ -663,13 +676,13 @@ class HistoryFileFolder(HistoryFolder):
663 676
         
664 677
         if self._content.type == ContentType.File:
665 678
             return HistoryFile(
666
-                path=self.path + str(rev.revision_id) + '-' + rev.file_name,
679
+                path=self.path + '/' + str(revision.revision_id) + '-' + revision.file_name,
667 680
                 environ=self.environ,
668 681
                 content=self._content, 
669 682
                 content_revision=revision)
670 683
         else:
671 684
             return HistoryOtherFile(
672
-                path=self.path + str(rev.revision_id) + '-' + rev.get_label(),
685
+                path=self.path + '/' + str(revision.revision_id) + '-' + revision.get_label(),
673 686
                 environ=self.environ,
674 687
                 content=self._content,
675 688
                 content_revision=revision)
@@ -677,9 +690,21 @@ class HistoryFileFolder(HistoryFolder):
677 690
     def delete(self):
678 691
         raise DAVError(HTTP_FORBIDDEN)
679 692
 
693
+    def getMemberList(self) -> [_DAVResource]:
694
+        memberlist = []
695
+
696
+        if self.environ['REQUEST_METHOD'] not in ['MOVE', 'COPY', 'DELETE']:
697
+
698
+            for name in self.getMemberNames():
699
+                member = self.getMember(name)
700
+                if member is not None:
701
+                    memberlist.append(member)
702
+
703
+        return memberlist
704
+
680 705
 
681 706
 class File(DAVNonCollection):
682
-    def __init__(self, path: str, environ: environ, content: Content, is_new_file: bool):
707
+    def __init__(self, path: str, environ: dict, content: Content, is_new_file: bool):
683 708
         super(File, self).__init__(path, environ)
684 709
 
685 710
         self._content = content
@@ -690,10 +715,15 @@ class File(DAVNonCollection):
690 715
             show_deleted=True
691 716
         )
692 717
 
693
-        self.filestream = MyFileStream(content=self._content, content_api=self._api)
718
+        self.filestream = FileStream(
719
+            content=self._content,
720
+            content_api=self._api,
721
+            file_name=self._content.get_label(),
722
+            new_file=False
723
+            )
694 724
 
695 725
     def __repr__(self) -> str:
696
-        return "<DAVNonCollectio: File (%s)>" % self._content.get_label()
726
+        return "<DAVNonCollection: File (%s)>" % self._content.get_label()
697 727
 
698 728
     def getContentLength(self) -> int:
699 729
         return len(self._content.file_content)
@@ -720,14 +750,18 @@ class File(DAVNonCollection):
720 750
 
721 751
         return filestream
722 752
 
723
-    def beginWrite(self, contentType: str=None) -> MyFileStream:
753
+    def beginWrite(self, contentType: str=None) -> FileStream:
724 754
         return self.filestream
725 755
 
726 756
     def copyMoveSingle(self, destpath: str, ismove: bool):
727 757
         destpath = normpath(destpath)
728 758
 
729 759
         if ismove:
730
-            parent = self.provider.get_parent_from_path(normpath(destpath), self._api, WorkspaceApi(self._user))
760
+            parent = self.provider.get_parent_from_path(
761
+                normpath(destpath),
762
+                self._api,
763
+                WorkspaceApi(self.environ['user'])
764
+            )
731 765
 
732 766
             with new_revision(self._content):
733 767
                 if basename(destpath) != self._content.label:
@@ -801,7 +835,7 @@ class HistoryFile(File):
801 835
         raise DAVError(HTTP_FORBIDDEN)
802 836
 
803 837
     def handleDelete(self):
804
-        return True
838
+        return False
805 839
 
806 840
     def handleCopy(self, destPath, depthInfinity):
807 841
         return True
@@ -825,9 +859,9 @@ class OtherFile(File):
825 859
 
826 860
     def getContentLength(self) -> int:
827 861
         if self._content.type == ContentType.Page:
828
-            return len(self.designPage(self._content))
862
+            return len(designPage(self._content, self._content.revision))
829 863
         else:
830
-            return len(self.designThread(self._content))
864
+            return len(designThread(self._content, self._content.revision, self._api.get_all(self._content.content_id, ContentType.Comment)))
831 865
 
832 866
     def getContentType(self) -> str:
833 867
         return 'text/html'
@@ -839,232 +873,14 @@ class OtherFile(File):
839 873
             filestream = compat.StringIO()
840 874
 
841 875
         if self._content.type == ContentType.Page:
842
-            self._content_page = self.designPage(self._content)
876
+            self._content_page = designPage(self._content, self._content.revision)
843 877
         else:
844
-            self._content_page = self.designThread(self._content)
878
+            self._content_page = designThread(self._content, self._content.revision, self._api.get_all(self._content.content_id, ContentType.Comment))
845 879
 
846 880
         filestream.write(bytes(self._content_page, 'utf-8'))
847 881
         filestream.seek(0)
848 882
         return filestream
849 883
 
850
-    def designPage(self, content: data.Content) -> str:
851
-        #f = open('wsgidav/addons/webdav/style.css', 'r')
852
-        style = ''#f.read()
853
-        #f.close()
854
-
855
-        hist = self._content.get_history()
856
-        histHTML = '<table class="table table-striped table-hover">'
857
-        for event in hist:
858
-            if isinstance(event, VirtualEvent):
859
-                date = event.create_readable_date()
860
-                _LABELS = {
861
-                    'archiving': 'Item archived',
862
-                    'content-comment': 'Item commented',
863
-                    'creation': 'Item created',
864
-                    'deletion': 'Item deleted',
865
-                    'edition': 'item modified',
866
-                    'revision': 'New revision',
867
-                    'status-update': 'New status',
868
-                    'unarchiving': 'Item unarchived',
869
-                    'undeletion': 'Item undeleted',
870
-                    'move': 'Item moved'
871
-                }
872
-
873
-                label = _LABELS[event.type.id]
874
-
875
-                histHTML += '''
876
-                <tr class="%s">
877
-                    <td class="my-align"><span class="label label-default"><i class="fa %s"></i> %s</span></td>
878
-                    <td>%s</td>
879
-                    <td>%s</td>
880
-                    <td>%s</td>
881
-                </tr>
882
-                ''' % ('warning' if event.id == content.revision_id else '',
883
-                       event.type.icon,
884
-                       label,
885
-                       date,
886
-                       event.owner.display_name,
887
-                       '<i class="fa fa-caret-left"></i> shown' if event.id == content.revision_id else '''<span><a class="revision-link" href="/.history/%s/%s-%s">(View revision)</a></span>''' % (self._content.label, event.id, event.ref_object.label) if event.type.id == 'revision' else '')
888
-
889
-        histHTML+='</table>'
890
-
891
-        file = '''
892
-<html>
893
-<head>
894
-	<title>%s</title>
895
-	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
896
-	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
897
-	<link rel="stylesheet" href="/home/arnaud/Documents/css/style.css">
898
-	<script type="text/javascript" src="/home/arnaud/Documents/css/script.js"></script>
899
-</head>
900
-<body>
901
-    <div id="left" class="col-lg-8 col-md-12 col-sm-12 col-xs-12">
902
-        <div class="title page">
903
-            <div class="title-text">
904
-                <i class="fa fa-file-text-o title-icon page"></i>
905
-                <h1>%s</h1>
906
-                <h6>page created on <b>%s</b> by <b>%s</b></h6>
907
-            </div>
908
-            <div class="pull-right">
909
-                <div class="btn-group btn-group-vertical">
910
-                    <a class="btn btn-default">
911
-                        <i class="fa fa-external-link"></i> View in tracim</a>
912
-                    </a>
913
-                </div>
914
-            </div>
915
-        </div>
916
-        <div class="content col-xs-12 col-sm-12 col-md-12 col-lg-12">
917
-            %s
918
-        </div>
919
-    </div>
920
-    <div id="right" class="col-lg-4 col-md-12 col-sm-12 col-xs-12">
921
-        <h4>History</h4>
922
-        %s
923
-    </div>
924
-    <script type="text/javascript">
925
-        window.onload = function() {
926
-            elems = document.getElementsByClassName('revision-link');
927
-            for(var i = 0; i<elems.length; i++) {
928
-                test = window.location.href
929
-                test += "/.." + elems[i].href.replace(/file:\/\//, "")
930
-                elems[i].href = test
931
-            }
932
-        }
933
-    </script>
934
-</body>
935
-</html>
936
-        ''' % (content.label,
937
-               content.label,
938
-               self._content.created.strftime("%B %d, %Y at %H:%m"),
939
-               self._content.owner.display_name,
940
-               content.description,
941
-               histHTML)
942
-
943
-        return file
944
-
945
-    def designThread(self, content: data.Content) -> str:
946
-
947
-        comments = self._api.get_all(self._content.content_id, ContentType.Comment)
948
-        hist = self._content.get_history()
949
-
950
-        allT = []
951
-        allT += comments
952
-        allT += hist
953
-        allT.sort(key=lambda x: x.created, reverse=True)
954
-
955
-        disc = ''
956
-        participants = {}
957
-        for t in allT:
958
-            if t.type == ContentType.Comment:
959
-                disc += '''
960
-                    <div class="row comment comment-row">
961
-                        <i class="fa fa-comment-o comment-icon"></i>
962
-                            <div class="comment-content">
963
-                            <h5>
964
-                                <span class="comment-author"><b>%s</b> wrote :</span>
965
-                                <div class="pull-right text-right">%s</div>
966
-                            </h5>
967
-                            %s
968
-                        </div>
969
-                    </div>
970
-                    ''' % (t.owner.display_name, t.create_readable_date(), t.description)
971
-
972
-                if t.owner.display_name not in participants:
973
-                    participants[t.owner.display_name] = [1, t.created]
974
-                else:
975
-                    participants[t.owner.display_name][0] += 1
976
-            else:
977
-                if isinstance(t, VirtualEvent) and t.type.id != 'comment':
978
-                    _LABELS = {
979
-                        'archiving': 'Item archived',
980
-                        'content-comment': 'Item commented',
981
-                        'creation': 'Item created',
982
-                        'deletion': 'Item deleted',
983
-                        'edition': 'item modified',
984
-                        'revision': 'New revision',
985
-                        'status-update': 'New status',
986
-                        'unarchiving': 'Item unarchived',
987
-                        'undeletion': 'Item undeleted',
988
-                        'move': 'Item moved',
989
-                        'comment' : 'hmmm'
990
-                    }
991
-
992
-                    label = _LABELS[t.type.id]
993
-
994
-                    disc += '''
995
-                    <div class="row comment comment-row">
996
-                        <i class="fa %s comment-icon"></i>
997
-                            <div class="comment-content">
998
-                            <h5>
999
-                                <span class="comment-author"><b>%s</b></span>
1000
-                                <div class="pull-right text-right">%s</div>
1001
-                            </h5>
1002
-                            %s %s
1003
-                        </div>
1004
-                    </div>
1005
-                    ''' % (t.type.icon,
1006
-                           t.owner.display_name,
1007
-                           t.create_readable_date(),
1008
-                           label,
1009
-                           '''<span><a class="revision-link" href="/.history/%s/%s-%s">(View revision)</a></span>''' % (
1010
-                               self._content.label,
1011
-                               t.id,
1012
-                               t.ref_object.label) if t.type.id == 'revision' else '')
1013
-
1014
-        page = '''
1015
-<html>
1016
-<head>
1017
-	<title>%s</title>
1018
-	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
1019
-	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
1020
-	<link rel="stylesheet" href="/home/arnaud/Documents/css/style.css">
1021
-	<script type="text/javascript" src="/home/arnaud/Documents/css/script.js"></script>
1022
-</head>
1023
-<body>
1024
-    <div id="left" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
1025
-        <div class="title thread">
1026
-            <div class="title-text">
1027
-                <i class="fa fa-comments-o title-icon thread"></i>
1028
-                <h1>%s</h1>
1029
-                <h6>thread created on <b>%s</b> by <b>%s</b></h6>
1030
-            </div>
1031
-            <div class="pull-right">
1032
-                <div class="btn-group btn-group-vertical">
1033
-                    <a class="btn btn-default">
1034
-                        <i class="fa fa-external-link"></i> View in tracim</a>
1035
-                    </a>
1036
-                </div>
1037
-            </div>
1038
-        </div>
1039
-        <div class="content col-xs-12 col-sm-12 col-md-12 col-lg-12">
1040
-            <div class="description">
1041
-                <span class="description-text">%s</span>
1042
-            </div>
1043
-            %s
1044
-        </div>
1045
-    </div>
1046
-    <script type="text/javascript">
1047
-        window.onload = function() {
1048
-            elems = document.getElementsByClassName('revision-link');
1049
-            for(var i = 0; i<elems.length; i++) {
1050
-                test = window.location.href
1051
-                test += "/.." + elems[i].href.replace(/file:\/\//, "")
1052
-                elems[i].href = test
1053
-            }
1054
-        }
1055
-    </script>
1056
-</body>
1057
-</html>
1058
-        ''' % (content.label,
1059
-               content.label,
1060
-               self._content.created.strftime("%B %d, %Y at %H:%m"),
1061
-               self._content.owner.display_name,
1062
-               content.description,
1063
-               disc)
1064
-
1065
-        return page
1066
-
1067
-
1068 884
 class HistoryOtherFile(OtherFile):
1069 885
     def __init__(self, path: str, environ: dict, content: data.Content, content_revision: data.ContentRevisionRO):
1070 886
         super(HistoryOtherFile, self).__init__(path, environ, content)
@@ -1083,9 +899,9 @@ class HistoryOtherFile(OtherFile):
1083 899
             filestream = compat.StringIO()
1084 900
 
1085 901
         if self._content.type == ContentType.Page:
1086
-            self._content_page = self.designPage(self._content_revision)
902
+            self._content_page = designPage(self._content, self._content_revision)
1087 903
         else:
1088
-            self._content_page = self.designThread(self._content_revision)
904
+            self._content_page = designThread(self._content, self._content_revision, self._api.get_all(self._content.content_id, ContentType.Comment))
1089 905
 
1090 906
         filestream.write(bytes(self._content_page, 'utf-8'))
1091 907
         filestream.seek(0)
@@ -1111,3 +927,11 @@ class HistoryOtherFile(OtherFile):
1111 927
 
1112 928
     def copyMoveSingle(self, destpath, ismove):
1113 929
         raise DAVError(HTTP_FORBIDDEN)
930
+
931
+
932
+_CONTENTS = {
933
+    ContentType.Folder: Folder,
934
+    ContentType.File: File,
935
+    ContentType.Page: OtherFile,
936
+    ContentType.Thread: OtherFile
937
+}

+ 1 - 1
tracim/tracim/model/data.py View File

@@ -1235,7 +1235,7 @@ class VirtualEvent(object):
1235 1235
         self.content = content
1236 1236
         self.ref_object = ref_object
1237 1237
 
1238
-        print(type)
1238
+        # todo moi ? print(type)
1239 1239
         assert hasattr(type, 'id')
1240 1240
         assert hasattr(type, 'css')
1241 1241
         assert hasattr(type, 'icon')