瀏覽代碼

remove useless commented code + share properties + link node with owner

Damien Accorsi 11 年之前
父節點
當前提交
ecb57a303a
共有 2 個文件被更改,包括 377 次插入70 次删除
  1. 44 70
      pboard/pboard/model/data.py
  2. 333 0
      pboard/pboard/templates/document-widgets-tabs.mak

+ 44 - 70
pboard/pboard/model/data.py 查看文件

@@ -9,6 +9,7 @@ from hashlib import sha256
9 9
 import bs4
10 10
 from sqlalchemy import Table, ForeignKey, Column, Sequence
11 11
 from sqlalchemy.types import Unicode, Integer, DateTime, Text, LargeBinary
12
+import sqlalchemy.types as sqlat
12 13
 from sqlalchemy.orm import relation, synonym, relationship
13 14
 from sqlalchemy.orm import backref
14 15
 import sqlalchemy.orm as sqlao
@@ -18,40 +19,7 @@ from tg.i18n import ugettext as _, lazy_ugettext as l_
18 19
 
19 20
 import tg
20 21
 from pboard.model import DeclarativeBase, metadata, DBSession
21
-
22
-# This is the association table for the many-to-many relationship between
23
-# groups and permissions.
24
-"""pod_node_table = Table('pod_nodes', metadata,
25
-    Column('node_id', Integer, Sequence('pod_nodes__node_id__sequence'), primary_key=True),
26
-    Column('parent_id', Integer, ForeignKey('pod_nodes.node_id'), nullable=True, default=None),
27
-    Column('node_order', Integer, nullable=True, default=1),
28
-    Column('node_type',   Unicode(16), unique=False, nullable=False, default='data'),
29
-    Column('node_status', Unicode(16), unique=False, nullable=False, default='new'),
30
-
31
-    Column('created_at', DateTime, unique=False, nullable=False),
32
-    Column('updated_at', DateTime, unique=False, nullable=False),
33
-
34
-    Column('data_label',   Unicode(1024), unique=False, nullable=False, default=''),
35
-    Column('data_content', Text(), unique=False, nullable=False, default=''),
36
-    Column('data_datetime', DateTime, unique=False, nullable=False),
37
-    Column('data_reminder_datetime', DateTime, unique=False, nullable=True),
38
-    
39
-    Column('data_file_name', Unicode(255), unique=False, nullable=False, default=''),
40
-    Column('data_file_mime_type', Unicode(255), unique=False, nullable=False, default=''),
41
-    Column('data_file_content', LargeBinary(), unique=False, nullable=False, default=None),
42
-)
43
-"""
44
-"""
45
-- node_type
46
-
47
-- node_created_at
48
-- node_updated_at
49
-
50
-- data_label
51
-- data_content
52
-- data_source_url
53
-- data_status_id
54
-"""
22
+from pboard.model import auth as pma
55 23
 
56 24
 class PBNodeStatusItem(object):
57 25
   def __init__(self, psStatusId, psStatusLabel, psStatusFamily, psIconId, psCssClass): #, psBackgroundColor):
@@ -143,8 +111,7 @@ class PBNodeStatus(object):
143 111
       PBNodeStatus.StatusList['closed'],
144 112
       PBNodeStatus.StatusList['deleted']
145 113
     ]
146
-    
147
-    PBNodeStatus.StatusList.values()
114
+
148 115
     
149 116
   @classmethod
150 117
   def getStatusItem(cls, psStatusId):
@@ -182,6 +149,7 @@ class PBNode(DeclarativeBase):
182 149
     return len(self._lStaticChildList)
183 150
 
184 151
   __tablename__ = 'pod_nodes'
152
+
185 153
   node_id          = Column(Integer, Sequence('pod_nodes__node_id__sequence'), primary_key=True)
186 154
   parent_id        = Column(Integer, ForeignKey('pod_nodes.node_id'), nullable=True, default=None)
187 155
   node_depth       = Column(Integer, unique=False, nullable=False, default=0)
@@ -195,6 +163,21 @@ class PBNode(DeclarativeBase):
195 163
   created_at = Column(DateTime, unique=False, nullable=False)
196 164
   updated_at = Column(DateTime, unique=False, nullable=False)
197 165
 
166
+  """
167
+    if 1, the document is available for other users logged into pod.
168
+    default is 0 (private document)
169
+  """
170
+  is_shared = Column(sqlat.Boolean, unique=False, nullable=False, default=False)
171
+  """
172
+    if 1, the document is available through a public - but obfuscated, url
173
+    default is 0 (document not publicly available)
174
+  """
175
+  is_public = Column(sqlat.Boolean, unique=False, nullable=False, default=False)
176
+  """
177
+    here is the hash allowing to get the document publicly
178
+  """
179
+  public_url_key = Column(Unicode(1024), unique=False, nullable=False, default='')
180
+
198 181
   data_label   = Column(Unicode(1024), unique=False, nullable=False, default='')
199 182
   data_content = Column(Text(),        unique=False, nullable=False, default='')
200 183
   
@@ -207,6 +190,7 @@ class PBNode(DeclarativeBase):
207 190
 
208 191
 
209 192
   _oParent = relationship('PBNode', remote_side=[node_id], backref='_lAllChildren')
193
+  _oOwner = relationship('User', remote_side=[pma.User.user_id], backref='_lAllNodes')
210 194
 
211 195
   def getChildrenOfType(self, plNodeTypeList, poKeySortingMethod=None, pbDoReverseSorting=False):
212 196
     """return all children nodes of type 'data' or 'node' or 'folder'"""
@@ -230,9 +214,15 @@ class PBNode(DeclarativeBase):
230 214
   def getChildNb(self):
231 215
     return self.getChildNbOfType([PBNodeType.Data])
232 216
 
233
-  def getChildren(self):
217
+  def getChildren(self, pbIncludeDeleted=False):
234 218
     """return all children nodes of type 'data' or 'node' or 'folder'"""
235
-    return self.getChildrenOfType([PBNodeType.Node, PBNodeType.Folder, PBNodeType.Data])
219
+    # return self.getChildrenOfType([PBNodeType.Node, PBNodeType.Folder, PBNodeType.Data])
220
+    items = self.getChildrenOfType([PBNodeType.Node, PBNodeType.Folder, PBNodeType.Data])
221
+    items2 = list()
222
+    for item in items:
223
+      if pbIncludeDeleted==True or item.node_status!='deleted':
224
+        items2.append(item)
225
+    return items2
236 226
 
237 227
   def getContacts(self):
238 228
     """return all children nodes of type 'data' or 'node' or 'folder'"""
@@ -343,13 +333,23 @@ class PBNode(DeclarativeBase):
343 333
           break
344 334
       return PBNodeStatus.getStatusItem(lsRealStatusId)
345 335
 
346
-  def getTruncatedLabel(self, piCharNb):
347
-    lsTruncatedLabel = ''
336
+  def getTruncatedLabel(self: PBNode, piCharNb: int):
337
+    """
338
+    return a truncated version of the data_label property.
339
+    if piCharNb is not > 0, then the full data_label is returned
340
+    note: if the node is a file and the data_label is empty, the file name is returned
341
+    """
342
+    lsTruncatedLabel = self.data_label
343
+
344
+    # 2014-05-06 - D.A. - HACK
345
+    # if the node is a file and label empty, then use the filename as data_label
346
+    if self.node_type==PBNodeType.File and lsTruncatedLabel=='':
347
+      lsTruncatedLabel = self.data_file_name
348
+
348 349
     liMaxLength = int(piCharNb)
349
-    if len(self.data_label)>liMaxLength:
350
-      lsTruncatedLabel = self.data_label[0:liMaxLength-1]+'…'
351
-    else:
352
-      lsTruncatedLabel = self.data_label
350
+    if liMaxLength>0 and len(lsTruncatedLabel)>liMaxLength:
351
+      lsTruncatedLabel = lsTruncatedLabel[0:liMaxLength-1]+'…'
352
+
353 353
     return lsTruncatedLabel
354 354
 
355 355
   def getTruncatedContentAsText(self, piCharNb):
@@ -387,29 +387,3 @@ class PBNode(DeclarativeBase):
387 387
     # Does not match @@ at end of content.
388 388
 
389 389
 
390
-
391
-"""from sqlalchemy.orm import mapper
392
-mapper(
393
-  PBNode,
394
-  pod_node_table,
395
-  properties = {'_lAllChildren' : relationship(PBNode, backref=backref('_oParent', remote_side=PBNode.parent_id)) }
396
-)"""
397
-
398
-
399
-
400
-"""    children = relationship('TreeNode',
401
-
402
-                        # cascade deletions
403
-                        cascade="all",
404
-
405
-                        # many to one + adjacency list - remote_side
406
-                        # is required to reference the 'remote' 
407
-                        # column in the join condition.
408
-                        backref=backref("parent", remote_side='TreeNode.id'),
409
-
410
-                        # children will be represented as a dictionary
411
-                        # on the "name" attribute.
412
-                        collection_class=attribute_mapped_collection('name'),
413
-                    ) 
414
-"""
415
-

+ 333 - 0
pboard/pboard/templates/document-widgets-tabs.mak 查看文件

@@ -0,0 +1,333 @@
1
+<%inherit file="local:templates.master"/>
2
+<%namespace name="POD" file="pboard.templates.pod"/>
3
+<%namespace name="DOC" file="pboard.templates.document-widgets"/>
4
+
5
+<%def name="AccessManagementTab(poNode)">
6
+  ######
7
+  ##
8
+  ## THIS WIDGET IS INTENDED TO BE USED ONE TIME ONLY IN A PAGE
9
+  ##
10
+  <h4>${_('Share options')}</h4> 
11
+  <p>
12
+    This document is
13
+    % if poNode.is_shared==False:
14
+      <span class="label label-info">
15
+        <i class="fa fa-user"></i>
16
+        ${_('private')}
17
+      </span>
18
+    % else:
19
+      <span class="label label-info">
20
+        <i class="fa fa-group"></i>
21
+        ${_('collaborative')}
22
+      </span>
23
+    % endif
24
+  </p>
25
+  <p>
26
+    % if poNode.is_shared==True or poNode.is_shared==False:
27
+      ${_('People working on it are:')}
28
+######
29
+##
30
+## FIXME - SHOW LIST OF GROUPS ALLOWED TO WORK ON THE DOCUMENT
31
+##
32
+    <table class="table table-striped table-hover table-condensed">
33
+      <thead>
34
+        <tr>
35
+          <th><i class="fa fa-group"></i> ${_('Groups')}</th>
36
+          <th></th>
37
+        </tr>
38
+      </thead>
39
+      <tr>
40
+        <td>Recherche et Développement</td>
41
+        <td>
42
+          <span class="label label-success" title="${_('Read access')}">R</span>
43
+          <span class="label label-warning" title="${_('Write access')}">W</span>
44
+        </td>
45
+      </tr>
46
+      <thead>
47
+        <tr>
48
+          <th><i class="fa fa-user"></i> ${_('Users')}</th>
49
+          <th></th>
50
+        </tr>
51
+      </thead>
52
+      <tr>
53
+        <td>Damien Accorsi</td>
54
+        <td>
55
+          <span class="label label-success">R</span>
56
+        </td>
57
+      </tr>
58
+      <tr>
59
+        <td>Sylvain Ferot</td>
60
+        <td>
61
+          <span class="label label-success">R</span>
62
+          <span class="label label-warning">W</span>
63
+        </td>
64
+      </tr>
65
+    </table>
66
+    
67
+    % endif
68
+  <p>
69
+
70
+######
71
+##
72
+## 2014-05-06 - D.A. We do not share documents on internet yet.
73
+##
74
+##  <p>
75
+##    % if poNode.is_public==False:
76
+##      ${_('This document is not shared on internet')|n}
77
+##    % else:
78
+##      ${_('This document is <span class="label label-warning"><i class="fa fa-globe"></i><span>shared</span></span> on internet')|n}.
79
+##      ${_('The associated url is:')} <a href="FIXME">${poNode.public_url_key}</a>
80
+##    % endif
81
+##  </p>
82
+  <!-- Button to trigger modal -->
83
+  <a href="#edit-document-share-properties" role="button" class="btn btn-success" data-toggle="modal">
84
+    <i class="fa fa-edit"></i>
85
+    ${_('Edit share options')}
86
+  </a>
87
+     
88
+  <!-- Modal -->
89
+  <div
90
+    id="edit-document-share-properties"
91
+    class="modal hide"
92
+    tabindex="-1"
93
+    role="dialog"
94
+    aria-labelledby="myModalLabel"
95
+    aria-hidden="true">
96
+    
97
+    <div class="modal-header">
98
+      <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
99
+      <h3 id="myModalLabel">Document sharing options</h3>
100
+    </div>
101
+    <div class="modal-body">
102
+
103
+      <form id='document-share-form' method="GET" action="${tg.url('/api/set_access_management?node_id=%d'%poNode.node_id)}">
104
+        <fieldset>
105
+          <label class="checkbox">
106
+            <input name="is_shared" type="checkbox" id="document-share-selector" ${('', 'checked')[poNode.is_shared]}/>
107
+            ${_('Share document with collaborators.')} <i class="fa fa-group"></i>
108
+          </label>
109
+          <div id="document-share-people-selector">
110
+            <p>
111
+              ${_('Select read and write access for each group or people...')}</p>
112
+            <script>
113
+            function updateRights(psUserId) {
114
+              var ACCESS_NONE = '';
115
+              var ACCESS_READ = 'R';
116
+              var ACCESS_WRITE = 'RW';
117
+              
118
+              var nodeIdForSelectedUser = 'user-'+psUserId+'-value';
119
+              var widget = $('#'+nodeIdForSelectedUser);
120
+              var oldValue = widget.val();
121
+              var newValue = '';
122
+              if(oldValue==ACCESS_NONE) {
123
+                newValue = ACCESS_READ;
124
+                newHtml = '<span class="label label-success">R</span>';
125
+              } else if(oldValue==ACCESS_READ) {
126
+                newValue = ACCESS_WRITE;
127
+                newHtml = '<span class="label label-success">R</span> <span class="label label-warning">W</span>';
128
+              } else if (oldValue==ACCESS_WRITE) {
129
+                newValue = ACCESS_NONE;
130
+                newHtml = '';
131
+              } else {
132
+                newValue = ACCESS_READ;
133
+                newHtml = '<span class="label label-success">R</span>';
134
+              }
135
+              
136
+              widget.val(newValue);
137
+              visibleid = 'user-'+psUserId+'-rights';
138
+              $("#"+visibleid).html(newHtml);
139
+            }
140
+            </script>
141
+            
142
+            <table class="table table-striped table-hover table-condensed">
143
+              <thead>
144
+                <tr>
145
+                  <th></th>
146
+                  <th>${_('Group')}</th>
147
+                  <th>${_('Access')}</th>
148
+                </tr>
149
+              </thead>
150
+    ######
151
+    ##
152
+    ## FIXME - SET A DYNAMIC SELECT LIST HERE
153
+    ##
154
+              % for loCurrentUser in ((3, 'Research and Development'), (4, 'Sylvain Ferot'), (5, 'Damien Accorsi')):
155
+              <tr id='user-${loCurrentUser[0]}-rights-row'>
156
+                <td>
157
+                  <a
158
+                    class="btn btn-mini"
159
+                    onclick="updateRights(${loCurrentUser[0]})"
160
+                  >
161
+                    <i class="fa fa-key"></i>
162
+                  </a>
163
+                </td>
164
+                <td class='pod-highlightable-access-management-cell'>
165
+                  ${loCurrentUser[1]}
166
+                  <input
167
+                    type="hidden"
168
+                    id="user-${loCurrentUser[0]}-value"
169
+                    name="user[${loCurrentUser[0]}]"
170
+                    value=""
171
+                  />
172
+                </td>
173
+                <td id="user-${loCurrentUser[0]}-rights" class="pod-right-cell"></td>
174
+              </tr>
175
+              % endfor
176
+            </table>
177
+          </div>
178
+        </fieldset>
179
+######
180
+##
181
+## 2014-05-06 - D.A. The documents are not yet sharable through internet
182
+##
183
+##        <fieldset>
184
+##          <label class="checkbox">
185
+##            <input name="is_public" type="checkbox" id="document-public-selector" ${('', 'checked')[poNode.is_public]}/>
186
+##            ${_('Internet shared document')}
187
+##            <i class="fa fa-globe"></i>
188
+##          </label>
189
+##          <label id="document-public-key-selector">
190
+##            ${_('Key')}
191
+##            <div class="input-append">
192
+##              <input name="url_public_key" id="document-public-key" type="text">
193
+##              <span id="document-public-key-refresh-button" class="add-on btn" title="${_('Regenerate key')}">
194
+##                <i class="fa fa-refresh"></i>
195
+##              </span>
196
+##            </div>
197
+##            <p><a id='document-public-key-url' href="">http://share.pod.com/document/azefnzeioguneriugnreiugnre</a></p>
198
+##          </label>
199
+
200
+        </fieldset>
201
+####
202
+## Button replaced by modal dialog button
203
+##        <button type="submit" class="btn btn-success">
204
+##          <i class="fa fa-check"></i>
205
+##          ${_('Save')}
206
+##        </button>
207
+      </form>
208
+    </div>
209
+    <div class="modal-footer">
210
+    <button class="btn" data-dismiss="modal" aria-hidden="true">
211
+      <i class="fa fa-ban"></i> ${_('Cancel')}
212
+    </button>
213
+    <button class="btn btn-success" id="document-share-form-submit-button">
214
+      <i class="fa fa-check"></i> ${_('Save changes')}
215
+    </button>
216
+    </div>
217
+    <script>
218
+##
219
+## 2014-05-06 - D.A. - Documents are not yet sharable through internet
220
+##
221
+##        function refreshDocumentPublicKey(psNewPublicKey) {
222
+##          var lsNewUrl = 'http://share.pod.com/document/'+psNewPublicKey;
223
+##          $('#document-public-key').val(psNewPublicKey);
224
+##          $('#document-public-key-url').attr('href', lsNewUrl);
225
+##          $('#document-public-key-url').text(lsNewUrl);
226
+##        }
227
+      
228
+      function toggleDocumentSharePeopleSelector(pbShowIt) {
229
+        if (pbShowIt) {
230
+          $('#document-share-people-selector').show();
231
+          // $('#document-share-people-selector input').removeAttr("disabled");
232
+        } else {
233
+          $('#document-share-people-selector').hide();
234
+          // $('#document-share-people-selector input').prop('disabled', 'disabled');
235
+        }
236
+      }
237
+
238
+##
239
+## 2014-05-06 - D.A. - Documents are not yet sharable through internet
240
+##
241
+##        function toggleDocumentPublicKeyGenerator(pbShowIt) {
242
+##          if (pbShowIt) {
243
+##            $('#document-public-key-selector input').removeAttr("disabled");
244
+##            $('#document-public-key-refresh-button').removeProp('disabled');
245
+##            $('#document-public-key-refresh-button').removeClass('btn-disabled');
246
+##            $('#document-public-key-selector a').show();
247
+##            $('#document-public-key-refresh-button').on("click").click(function () {
248
+##              refreshDocumentPublicKey(generateStringId()); // New random 32-char id
249
+##            });
250
+##            if($('#document-public-key-selector input').val()=='') {
251
+##              refreshDocumentPublicKey(generateStringId());
252
+##            }
253
+##          } else {
254
+##            $('#document-public-key-refresh-button').prop('disabled', true);
255
+##            $('#document-public-key-refresh-button').addClass('btn-disabled');
256
+##            $('#document-public-key-selector input').prop('disabled', 'disabled');
257
+##            $('#document-public-key-refresh-button').off("click");
258
+##            $('#document-public-key-selector a').hide();
259
+##          }
260
+##        }
261
+##
262
+##
263
+##
264
+
265
+      // Callbacks setup
266
+      $('#document-share-selector').change(function () {
267
+        var checkedValue = $('#document-share-selector').prop("checked");
268
+        toggleDocumentSharePeopleSelector(checkedValue);
269
+      });
270
+
271
+##        $('#document-public-selector').change(function () {
272
+##          var checkedValue = $('#document-public-selector').prop("checked");
273
+##          toggleDocumentPublicKeyGenerator(checkedValue);
274
+##        });
275
+
276
+      // Submit access-management modal dialog form
277
+      $('#document-share-form-submit-button').click(function(){
278
+        $('#document-share-form')[0].submit();
279
+      });
280
+
281
+      // Initial setup
282
+      // Activate or disactivate users selector according
283
+      // to current state of the is_shared property
284
+      //
285
+      // FIXME - 2014-05-06 - This is not working (should be done at document.ready time)
286
+      // note: putting this in a document.ready callback does not work.
287
+      //
288
+      $('#document-share-form')[0].reset();
289
+      toggleDocumentSharePeopleSelector($('#document-share-selector').prop("checked"));
290
+##        toggleDocumentPublicKeyGenerator($('#document-public-selector').prop("checked"));  
291
+##        
292
+##        refreshDocumentPublicKey($('#document-public-key').val()); // First init
293
+
294
+    </script>
295
+  </div>
296
+</%def>
297
+
298
+<%def name="FilesTabContent(poNode)">
299
+  <h4>${_('Attachments')}</h4> 
300
+% if len(poNode.getFiles())<=0:
301
+  <p class="pod-grey">${_("There is currently no attachment.")}<br/></p>
302
+  <p>${POD.OpenModalButton('create-new-file-modal-form', _(' Attach first file'))}</p>
303
+% else:
304
+  <p>${POD.OpenModalButton('create-new-file-modal-form', _(' Attach a file'))}</p>
305
+% endif
306
+
307
+  ${DOC.FileEditModalDialog(poNode.node_id, None, tg.url('/api/create_file'), 'create-new-file-modal-form', 'Add a new file')}
308
+
309
+
310
+  <!-- LIST OF FILES -->
311
+  <div>
312
+    % if len(poNode.getFiles())>0:
313
+      % for loFile in poNode.getFiles():
314
+        <p style="list-style-type:none; margin-bottom: 0.5em;">
315
+          <i class="fa fa-paperclip"></i>
316
+          <a
317
+            href="${tg.url('/document/%i'%loFile.node_id)}"
318
+            title="${_('View the attachment')}: ${loFile.getTruncatedLabel(-1)}"
319
+          >
320
+            ${loFile.getTruncatedLabel(50)}
321
+          </a>
322
+          <a
323
+            class="pull-right"
324
+            href="${tg.url('/api/get_file_content/%s'%(loFile.node_id))}"
325
+            title="${_('View the attachment')}"
326
+          >
327
+            <i class="fa fa-download"></i>
328
+          </a>
329
+        </p>
330
+      % endfor
331
+    % endif
332
+  </div>
333
+</%def>