浏览代码

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

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

+ 3 - 5
tracim/tracim/lib/content.py 查看文件

364
 
364
 
365
         return self._base_query(workspace).filter(Content.content_id==content_id).filter(Content.type==content_type).one()
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
         This method allow us to get directly any revision with its id
369
         This method allow us to get directly any revision with its id
370
         :param revision_id: The content's revision's id that we want to return
370
         :param revision_id: The content's revision's id that we want to return
372
         """
372
         """
373
         assert revision_id is not None# DYN_REMOVE
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
     def get_one_by_label_and_parent(self, content_label: str, content_parent: Content = None,
379
     def get_one_by_label_and_parent(self, content_label: str, content_parent: Content = None,
382
                                     workspace: Workspace = None) -> Content:
380
                                     workspace: Workspace = None) -> Content:

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

20
         self._file_name = file_name if file_name != '' else self._content.file_name
20
         self._file_name = file_name if file_name != '' else self._content.file_name
21
         self._content = content
21
         self._content = content
22
         self._api = content_api
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
         return self
26
         return self
26
 
27
 
27
     def endWrite(self, withErrors: bool):
28
     def endWrite(self, withErrors: bool):
36
         for part in self._buffer:
37
         for part in self._buffer:
37
             item_content += part
38
             item_content += part
38
 
39
 
39
-        if new_file:
40
+        if self._is_new_file:
40
             file = self._api.create(
41
             file = self._api.create(
41
                 content_type=ContentType.File,
42
                 content_type=ContentType.File,
42
                 workspace=self._content.workspace,
43
                 workspace=self._content.workspace,

+ 277 - 0
tracim/tracim/lib/webdav/design.py 查看文件

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 查看文件

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

+ 167 - 58
tracim/tracim/lib/webdav/sql_dav_provider.py 查看文件

14
 from wsgidav.lock_manager import LockManager
14
 from wsgidav.lock_manager import LockManager
15
 from tracim.model.data import ContentType
15
 from tracim.model.data import ContentType
16
 
16
 
17
+from tracim.lib.content import ContentRevisionRO
17
 ######################################
18
 ######################################
18
 
19
 
19
 __docformat__ = "reStructuredText"
20
 __docformat__ = "reStructuredText"
39
         if manage_lock:
40
         if manage_lock:
40
             self.lockManager = LockManager(LockStorage())
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
     def __repr__(self):
56
     def __repr__(self):
43
         return 'Provider'
57
         return 'Provider'
44
 
58
 
45
     #########################################################
59
     #########################################################
46
     # Everything override from DAVProvider
60
     # Everything override from DAVProvider
47
     def getResourceInst(self, path, environ):
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
         workspace_api = WorkspaceApi(environ['user'])
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
         # is archive
96
         # is archive
73
         is_archived_folder = re.search(r'/\.archived$', norm_path) is not None
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
         # is delete
106
         # is delete
79
         is_deleted_folder = re.search(r'/\.deleted$', norm_path) is not None
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
         # is history
116
         # is history
85
         is_history_folder = re.search(r'/\.history$', norm_path) is not None
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
             is_deleted_folder = re.search(r'/\.deleted/\.history$', norm_path) is not None
120
             is_deleted_folder = re.search(r'/\.deleted/\.history$', norm_path) is not None
89
             is_archived_folder = re.search(r'/\.archived/\.history$', norm_path) is not None
121
             is_archived_folder = re.search(r'/\.archived/\.history$', norm_path) is not None
90
 
122
 
92
                 else HistoryType.Archived if is_archived_folder \
124
                 else HistoryType.Archived if is_archived_folder \
93
                 else HistoryType.Standard
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
         # is history
134
         # is history
98
         is_history_file_folder = re.search(r'/\.history/([^/]+)$', norm_path) is not None
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
         # is history
144
         # is history
104
         is_history_file = re.search(r'/\.history/[^/]+/(\d+)-.+', norm_path) is not None
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
             if content.type == ContentType.File:
151
             if content.type == ContentType.File:
111
                 return sql_resources.HistoryFile(path, environ, content, content_revision)
152
                 return sql_resources.HistoryFile(path, environ, content, content_revision)
115
         # other
156
         # other
116
         if content.type == ContentType.Folder:
157
         if content.type == ContentType.Folder:
117
             return sql_resources.Folder(path, environ, content)
158
             return sql_resources.Folder(path, environ, content)
118
-
119
-        if content.type == ContentType.File:
159
+        elif content.type == ContentType.File:
120
             return sql_resources.File(path, environ, content, False)
160
             return sql_resources.File(path, environ, content, False)
121
-        else:
161
+        elif content.type in [ContentType.Page, ContentType.Thread]:
122
             return sql_resources.OtherFile(path, environ, content)
162
             return sql_resources.OtherFile(path, environ, content)
163
+        else:
164
+            return None
123
 
165
 
124
     def exists(self, path, environ):
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
             current_user=environ['user'],
173
             current_user=environ['user'],
136
             show_archived=True,
174
             show_archived=True,
137
             show_deleted=True
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
         is_archived = re.search(r'/\.archived/(\.history/)?(?!\.history)[^/]*(/\.)?(history|deleted|archived)?$', norm_path) is not None
190
         is_archived = re.search(r'/\.archived/(\.history/)?(?!\.history)[^/]*(/\.)?(history|deleted|archived)?$', norm_path) is not None
144
 
191
 
148
 
195
 
149
         if revision_id:
196
         if revision_id:
150
             revision_id = revision_id.group(1)
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
         return content is not None \
202
         return content is not None \
158
             and content.is_deleted == is_deleted \
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
     def reduce_path(self, path):
206
     def reduce_path(self, path):
163
         path = re.sub(r'/\.archived', r'', path)
207
         path = re.sub(r'/\.archived', r'', path)
168
 
212
 
169
         return path
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
         workspace = self.get_workspace_from_path(path, workspace_api)
218
         workspace = self.get_workspace_from_path(path, workspace_api)
174
 
219
 
175
         if basename(dirname(path)) == workspace.label:
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
         else:
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
             if parent is not None:
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
             return None
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
     def get_parent_from_path(self, path, api: ContentApi, workspace_api: WorkspaceApi):
240
     def get_parent_from_path(self, path, api: ContentApi, workspace_api: WorkspaceApi):
187
         return self.get_content_from_path(dirname(path), api, workspace_api)
241
         return self.get_content_from_path(dirname(path), api, workspace_api)
189
     def get_workspace_from_path(self, path: str, api: WorkspaceApi):
243
     def get_workspace_from_path(self, path: str, api: WorkspaceApi):
190
         assert path.startswith('/')
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
     # Everything that transform path
253
     # Everything that transform path
494
         last_item.child_revision_id = new_item.id
552
         last_item.child_revision_id = new_item.id
495
 
553
 
496
         self.session.commit()'''
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 查看文件

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

+ 97 - 273
tracim/tracim/lib/webdav/sql_resources.py 查看文件

15
 from wsgidav.dav_error import DAVError, HTTP_FORBIDDEN
15
 from wsgidav.dav_error import DAVError, HTTP_FORBIDDEN
16
 from wsgidav.dav_provider import DAVCollection, DAVNonCollection
16
 from wsgidav.dav_provider import DAVCollection, DAVNonCollection
17
 from tracim.model import data, new_revision
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
 from tracim.model.data import ContentType
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
 class Encapsuler(object):
24
 class Encapsuler(object):
30
     def __init__(self, type: str, api: ContentApi, content: Content):
25
     def __init__(self, type: str, api: ContentApi, content: Content):
40
 
35
 
41
         self._type = type
36
         self._type = type
42
 
37
 
38
+        self._new_name = self.make_name()
39
+
43
     def action(self):
40
     def action(self):
44
         with new_revision(self._content):
41
         with new_revision(self._content):
45
             self._actions[self._type](self._content)
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
             self._api.save(self._content, self._type)
49
             self._api.save(self._content, self._type)
47
 
50
 
48
         transaction.commit()
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
 class Root(DAVCollection):
65
 class Root(DAVCollection):
52
     def __init__(self, path: str, environ: dict):
66
     def __init__(self, path: str, environ: dict):
69
     def getMemberNames(self) -> [str]:
83
     def getMemberNames(self) -> [str]:
70
         return [workspace.label for workspace in self._workspace_api.get_all()]
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
         workspace = self._workspace_api.get_one_by_label(label)
88
         workspace = self._workspace_api.get_one_by_label(label)
75
 
89
 
122
 
136
 
123
         return retlist
137
         return retlist
124
 
138
 
125
-    def getMember(self, content_label: str) -> DAVResource:
139
+    def getMember(self, content_label: str) -> _DAVResource:
126
 
140
 
127
         content = self._content_api.get_one_by_label_and_parent(
141
         content = self._content_api.get_one_by_label_and_parent(
128
             content_label=content_label,
142
             content_label=content_label,
130
         )
144
         )
131
 
145
 
132
         return _CONTENTS[content.type](
146
         return _CONTENTS[content.type](
133
-            path=self.path + content.get_label(),
147
+            path=self.path + '/' + content.get_label(),
134
             environ=self.environ,
148
             environ=self.environ,
135
             content=content
149
             content=content
136
             )
150
             )
138
     def createEmptyResource(self, name: str):
152
     def createEmptyResource(self, name: str):
139
         raise DAVError(HTTP_FORBIDDEN)
153
         raise DAVError(HTTP_FORBIDDEN)
140
 
154
 
141
-    def createCollection(self, label: str) -> Folder:
155
+    def createCollection(self, label: str) -> 'Folder':
142
 
156
 
143
         folder = self._content_api.create(
157
         folder = self._content_api.create(
144
             content_type=ContentType.Folder,
158
             content_type=ContentType.Folder,
158
 
172
 
159
         transaction.commit()
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
     def delete(self):
177
     def delete(self):
164
         raise DAVError(HTTP_FORBIDDEN)
178
         raise DAVError(HTTP_FORBIDDEN)
175
     def setLastModified(self, destpath, timestamp, dryrun):
189
     def setLastModified(self, destpath, timestamp, dryrun):
176
         return False
190
         return False
177
 
191
 
178
-    def getMemberList(self) -> [DAVResource]:
192
+    def getMemberList(self) -> [_DAVResource]:
179
         memberlist = []
193
         memberlist = []
180
 
194
 
181
         if self.environ['REQUEST_METHOD'] not in ['MOVE', 'COPY', 'DELETE']:
195
         if self.environ['REQUEST_METHOD'] not in ['MOVE', 'COPY', 'DELETE']:
187
             if memberlist != [] and self._file_count > 0:
201
             if memberlist != [] and self._file_count > 0:
188
                 memberlist.append(
202
                 memberlist.append(
189
                     HistoryFolder(
203
                     HistoryFolder(
190
-                        path=self.path + ".history",
204
+                        path=self.path + '/' + ".history",
191
                         environ=self.environ,
205
                         environ=self.environ,
192
-                        content=self._content,
206
+                        content=None,
193
                         type=HistoryType.Standard
207
                         type=HistoryType.Standard
194
                         )
208
                         )
195
                     )
209
                     )
196
 
210
 
197
             memberlist.append(
211
             memberlist.append(
198
                 DeletedFolder(
212
                 DeletedFolder(
199
-                    path=self.path + ".deleted",
213
+                    path=self.path + '/' + ".deleted",
200
                     environ=self.environ,
214
                     environ=self.environ,
201
-                    content=self._content
215
+                    content=None
202
                     )
216
                     )
203
                 )
217
                 )
204
 
218
 
205
             memberlist.append(
219
             memberlist.append(
206
                 ArchivedFolder(
220
                 ArchivedFolder(
207
-                    path=self.path + ".archived",
221
+                    path=self.path + '/' + ".archived",
208
                     environ=self.environ,
222
                     environ=self.environ,
209
-                    content=self._content
223
+                    content=None
210
                     )
224
                     )
211
                 )
225
                 )
212
 
226
 
245
 
259
 
246
         return retlist
260
         return retlist
247
 
261
 
248
-    def getMember(self, content_label: str) -> DAVResource:
262
+    def getMember(self, content_label: str) -> _DAVResource:
249
 
263
 
250
         content = self._api.get_one_by_label_and_parent(
264
         content = self._api.get_one_by_label_and_parent(
251
             content_label=content_label,
265
             content_label=content_label,
253
         )
267
         )
254
 
268
 
255
         return self.provider.getResourceInst(
269
         return self.provider.getResourceInst(
256
-            path=self.path + content.get_label(),
270
+            path=self.path + '/' + content.get_label(),
257
             environ=self.environ
271
             environ=self.environ
258
             )
272
             )
259
 
273
 
265
             new_file=True
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
         folder = self._api.create(ContentType.Folder, self._content.workspace, self._content, label)
284
         folder = self._api.create(ContentType.Folder, self._content.workspace, self._content, label)
271
 
285
 
281
 
295
 
282
         transaction.commit()
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
     def delete(self):
300
     def delete(self):
287
         Encapsuler(ActionDescription.DELETION, self._api, self._content).action()
301
         Encapsuler(ActionDescription.DELETION, self._api, self._content).action()
337
         #self.item.updated = datetime.fromtimestamp(timestamp)
351
         #self.item.updated = datetime.fromtimestamp(timestamp)
338
         #return True
352
         #return True
339
 
353
 
340
-    def getMemberList(self) -> [DAVResource]:
354
+    def getMemberList(self) -> [_DAVResource]:
341
         memberlist = []
355
         memberlist = []
342
 
356
 
343
         for name in self.getMemberNames():
357
         for name in self.getMemberNames():
349
             if memberlist != [] and self._file_count > 0:
363
             if memberlist != [] and self._file_count > 0:
350
                 memberlist.append(
364
                 memberlist.append(
351
                     HistoryFolder(
365
                     HistoryFolder(
352
-                        path=self.path + ".history",
366
+                        path=self.path + '/' + ".history",
353
                         environ=self.environ,
367
                         environ=self.environ,
354
                         content=self._content,
368
                         content=self._content,
355
                         type=HistoryType.Standard
369
                         type=HistoryType.Standard
358
 
372
 
359
             memberlist.append(
373
             memberlist.append(
360
                 DeletedFolder(
374
                 DeletedFolder(
361
-                    path=self.path + ".deleted",
375
+                    path=self.path + '/' + ".deleted",
362
                     environ=self.environ,
376
                     environ=self.environ,
363
                     content=self._content
377
                     content=self._content
364
                     )
378
                     )
366
 
380
 
367
             memberlist.append(
381
             memberlist.append(
368
                 ArchivedFolder(
382
                 ArchivedFolder(
369
-                    path=self.path + ".archived",
383
+                    path=self.path + '/' + ".archived",
370
                     environ=self.environ,
384
                     environ=self.environ,
371
                     content=self._content
385
                     content=self._content
372
                     )
386
                     )
374
 
388
 
375
         return memberlist
389
         return memberlist
376
 
390
 
377
-
378
 class HistoryFolder(Folder):
391
 class HistoryFolder(Folder):
379
     def __init__(self, path, environ, content: data.Content, type):
392
     def __init__(self, path, environ, content: data.Content, type):
380
         super(HistoryFolder, self).__init__(path, environ, content)
393
         super(HistoryFolder, self).__init__(path, environ, content)
399
     def getLastModified(self) -> float:
412
     def getLastModified(self) -> float:
400
         return mktime(datetime.now().timetuple())
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
         content = self._api.get_one_by_label_and_parent(
416
         content = self._api.get_one_by_label_and_parent(
404
             content_label=content_label,
417
             content_label=content_label,
405
             content_parent=self._content
418
             content_parent=self._content
406
         )
419
         )
407
 
420
 
408
         return HistoryFileFolder(
421
         return HistoryFileFolder(
409
-            path=self.path + content.get_label(),
422
+            path=self.path + '/' + content.get_label(),
410
             environ=self.environ,
423
             environ=self.environ,
411
             content=content)
424
             content=content)
412
 
425
 
442
     def setLastModified(self, destpath: str, timestamp: float, dryrun: bool):
455
     def setLastModified(self, destpath: str, timestamp: float, dryrun: bool):
443
         return False
456
         return False
444
 
457
 
445
-    def getMemberList(self) -> [HistoryFileFolder]:
458
+    def getMemberList(self) -> [_DAVResource]:
446
         memberlist = []
459
         memberlist = []
447
         for name in self.getMemberNames():
460
         for name in self.getMemberNames():
448
             member = self.getMember(name)
461
             member = self.getMember(name)
456
         super(DeletedFolder, self).__init__(path, environ, content, HistoryType.Deleted)
469
         super(DeletedFolder, self).__init__(path, environ, content, HistoryType.Deleted)
457
 
470
 
458
         self._api = ContentApi(
471
         self._api = ContentApi(
459
-            current_user=self._user,
472
+            current_user=environ['user'],
460
             show_deleted=True
473
             show_deleted=True
461
         )
474
         )
462
 
475
 
474
     def getLastModified(self) -> float:
487
     def getLastModified(self) -> float:
475
         return mktime(datetime.now().timetuple())
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
         content = self._api.get_one_by_label_and_parent(
492
         content = self._api.get_one_by_label_and_parent(
480
             content_label=content_label,
493
             content_label=content_label,
482
         )
495
         )
483
 
496
 
484
         return self.provider.getResourceInst(
497
         return self.provider.getResourceInst(
485
-            path=self.path + content.get_label(),
498
+            path=self.path + '/' + content.get_label(),
486
             environ=self.environ
499
             environ=self.environ
487
             )
500
             )
488
 
501
 
519
     def setLastModified(self, destpath: str, timestamp: float, dryrun: bool):
532
     def setLastModified(self, destpath: str, timestamp: float, dryrun: bool):
520
         return False
533
         return False
521
 
534
 
522
-    def getMemberList(self) -> [DAVResource]:
535
+    def getMemberList(self) -> [_DAVResource]:
523
         memberlist = []
536
         memberlist = []
524
 
537
 
525
         if self.environ['REQUEST_METHOD'] not in ['MOVE', 'COPY', 'DELETE']:
538
         if self.environ['REQUEST_METHOD'] not in ['MOVE', 'COPY', 'DELETE']:
532
             if self._file_count > 0:
545
             if self._file_count > 0:
533
                 memberlist.append(
546
                 memberlist.append(
534
                     HistoryFolder(
547
                     HistoryFolder(
535
-                        path=self.path + ".history",
548
+                        path=self.path + '/' + ".history",
536
                         environ=self.environ,
549
                         environ=self.environ,
537
                         content=self._content, 
550
                         content=self._content, 
538
                         type=HistoryType.Deleted
551
                         type=HistoryType.Deleted
565
     def getLastModified(self) -> float:
578
     def getLastModified(self) -> float:
566
         return mktime(datetime.now().timetuple())
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
         content = self._api.get_one_by_label_and_parent(
583
         content = self._api.get_one_by_label_and_parent(
571
             content_label=content_label,
584
             content_label=content_label,
573
         )
586
         )
574
 
587
 
575
         return self.provider.getResourceInst(
588
         return self.provider.getResourceInst(
576
-            path=self.path + content.get_label(),
589
+            path=self.path + '/' + content.get_label(),
577
             environ=self.environ
590
             environ=self.environ
578
             )
591
             )
579
 
592
 
610
     def setLastModified(self, destpath: str, timestamp: float, dryrun: bool):
623
     def setLastModified(self, destpath: str, timestamp: float, dryrun: bool):
611
         return False
624
         return False
612
 
625
 
613
-    def getMemberList(self) -> [DAVResource]:
626
+    def getMemberList(self) -> [_DAVResource]:
614
         memberlist = []
627
         memberlist = []
615
         
628
         
616
         if self.environ['REQUEST_METHOD'] not in ['MOVE', 'COPY', 'DELETE']:
629
         if self.environ['REQUEST_METHOD'] not in ['MOVE', 'COPY', 'DELETE']:
622
             if self._file_count > 0:
635
             if self._file_count > 0:
623
                 memberlist.append(
636
                 memberlist.append(
624
                     HistoryFolder(
637
                     HistoryFolder(
625
-                        path=self.path + ".history",
638
+                        path=self.path + '/' + ".history",
626
                         environ=self.environ,
639
                         environ=self.environ,
627
                         content=self._content,
640
                         content=self._content,
628
                         type=HistoryType.Archived
641
                         type=HistoryType.Archived
663
         
676
         
664
         if self._content.type == ContentType.File:
677
         if self._content.type == ContentType.File:
665
             return HistoryFile(
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
                 environ=self.environ,
680
                 environ=self.environ,
668
                 content=self._content, 
681
                 content=self._content, 
669
                 content_revision=revision)
682
                 content_revision=revision)
670
         else:
683
         else:
671
             return HistoryOtherFile(
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
                 environ=self.environ,
686
                 environ=self.environ,
674
                 content=self._content,
687
                 content=self._content,
675
                 content_revision=revision)
688
                 content_revision=revision)
677
     def delete(self):
690
     def delete(self):
678
         raise DAVError(HTTP_FORBIDDEN)
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
 class File(DAVNonCollection):
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
         super(File, self).__init__(path, environ)
708
         super(File, self).__init__(path, environ)
684
 
709
 
685
         self._content = content
710
         self._content = content
690
             show_deleted=True
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
     def __repr__(self) -> str:
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
     def getContentLength(self) -> int:
728
     def getContentLength(self) -> int:
699
         return len(self._content.file_content)
729
         return len(self._content.file_content)
720
 
750
 
721
         return filestream
751
         return filestream
722
 
752
 
723
-    def beginWrite(self, contentType: str=None) -> MyFileStream:
753
+    def beginWrite(self, contentType: str=None) -> FileStream:
724
         return self.filestream
754
         return self.filestream
725
 
755
 
726
     def copyMoveSingle(self, destpath: str, ismove: bool):
756
     def copyMoveSingle(self, destpath: str, ismove: bool):
727
         destpath = normpath(destpath)
757
         destpath = normpath(destpath)
728
 
758
 
729
         if ismove:
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
             with new_revision(self._content):
766
             with new_revision(self._content):
733
                 if basename(destpath) != self._content.label:
767
                 if basename(destpath) != self._content.label:
801
         raise DAVError(HTTP_FORBIDDEN)
835
         raise DAVError(HTTP_FORBIDDEN)
802
 
836
 
803
     def handleDelete(self):
837
     def handleDelete(self):
804
-        return True
838
+        return False
805
 
839
 
806
     def handleCopy(self, destPath, depthInfinity):
840
     def handleCopy(self, destPath, depthInfinity):
807
         return True
841
         return True
825
 
859
 
826
     def getContentLength(self) -> int:
860
     def getContentLength(self) -> int:
827
         if self._content.type == ContentType.Page:
861
         if self._content.type == ContentType.Page:
828
-            return len(self.designPage(self._content))
862
+            return len(designPage(self._content, self._content.revision))
829
         else:
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
     def getContentType(self) -> str:
866
     def getContentType(self) -> str:
833
         return 'text/html'
867
         return 'text/html'
839
             filestream = compat.StringIO()
873
             filestream = compat.StringIO()
840
 
874
 
841
         if self._content.type == ContentType.Page:
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
         else:
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
         filestream.write(bytes(self._content_page, 'utf-8'))
880
         filestream.write(bytes(self._content_page, 'utf-8'))
847
         filestream.seek(0)
881
         filestream.seek(0)
848
         return filestream
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
 class HistoryOtherFile(OtherFile):
884
 class HistoryOtherFile(OtherFile):
1069
     def __init__(self, path: str, environ: dict, content: data.Content, content_revision: data.ContentRevisionRO):
885
     def __init__(self, path: str, environ: dict, content: data.Content, content_revision: data.ContentRevisionRO):
1070
         super(HistoryOtherFile, self).__init__(path, environ, content)
886
         super(HistoryOtherFile, self).__init__(path, environ, content)
1083
             filestream = compat.StringIO()
899
             filestream = compat.StringIO()
1084
 
900
 
1085
         if self._content.type == ContentType.Page:
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
         else:
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
         filestream.write(bytes(self._content_page, 'utf-8'))
906
         filestream.write(bytes(self._content_page, 'utf-8'))
1091
         filestream.seek(0)
907
         filestream.seek(0)
1111
 
927
 
1112
     def copyMoveSingle(self, destpath, ismove):
928
     def copyMoveSingle(self, destpath, ismove):
1113
         raise DAVError(HTTP_FORBIDDEN)
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 查看文件

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