Browse Source

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

Damien Accorsi 11 years ago
parent
commit
ecb57a303a
2 changed files with 377 additions and 70 deletions
  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 View File

9
 import bs4
9
 import bs4
10
 from sqlalchemy import Table, ForeignKey, Column, Sequence
10
 from sqlalchemy import Table, ForeignKey, Column, Sequence
11
 from sqlalchemy.types import Unicode, Integer, DateTime, Text, LargeBinary
11
 from sqlalchemy.types import Unicode, Integer, DateTime, Text, LargeBinary
12
+import sqlalchemy.types as sqlat
12
 from sqlalchemy.orm import relation, synonym, relationship
13
 from sqlalchemy.orm import relation, synonym, relationship
13
 from sqlalchemy.orm import backref
14
 from sqlalchemy.orm import backref
14
 import sqlalchemy.orm as sqlao
15
 import sqlalchemy.orm as sqlao
18
 
19
 
19
 import tg
20
 import tg
20
 from pboard.model import DeclarativeBase, metadata, DBSession
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
 class PBNodeStatusItem(object):
24
 class PBNodeStatusItem(object):
57
   def __init__(self, psStatusId, psStatusLabel, psStatusFamily, psIconId, psCssClass): #, psBackgroundColor):
25
   def __init__(self, psStatusId, psStatusLabel, psStatusFamily, psIconId, psCssClass): #, psBackgroundColor):
143
       PBNodeStatus.StatusList['closed'],
111
       PBNodeStatus.StatusList['closed'],
144
       PBNodeStatus.StatusList['deleted']
112
       PBNodeStatus.StatusList['deleted']
145
     ]
113
     ]
146
-    
147
-    PBNodeStatus.StatusList.values()
114
+
148
     
115
     
149
   @classmethod
116
   @classmethod
150
   def getStatusItem(cls, psStatusId):
117
   def getStatusItem(cls, psStatusId):
182
     return len(self._lStaticChildList)
149
     return len(self._lStaticChildList)
183
 
150
 
184
   __tablename__ = 'pod_nodes'
151
   __tablename__ = 'pod_nodes'
152
+
185
   node_id          = Column(Integer, Sequence('pod_nodes__node_id__sequence'), primary_key=True)
153
   node_id          = Column(Integer, Sequence('pod_nodes__node_id__sequence'), primary_key=True)
186
   parent_id        = Column(Integer, ForeignKey('pod_nodes.node_id'), nullable=True, default=None)
154
   parent_id        = Column(Integer, ForeignKey('pod_nodes.node_id'), nullable=True, default=None)
187
   node_depth       = Column(Integer, unique=False, nullable=False, default=0)
155
   node_depth       = Column(Integer, unique=False, nullable=False, default=0)
195
   created_at = Column(DateTime, unique=False, nullable=False)
163
   created_at = Column(DateTime, unique=False, nullable=False)
196
   updated_at = Column(DateTime, unique=False, nullable=False)
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
   data_label   = Column(Unicode(1024), unique=False, nullable=False, default='')
181
   data_label   = Column(Unicode(1024), unique=False, nullable=False, default='')
199
   data_content = Column(Text(),        unique=False, nullable=False, default='')
182
   data_content = Column(Text(),        unique=False, nullable=False, default='')
200
   
183
   
207
 
190
 
208
 
191
 
209
   _oParent = relationship('PBNode', remote_side=[node_id], backref='_lAllChildren')
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
   def getChildrenOfType(self, plNodeTypeList, poKeySortingMethod=None, pbDoReverseSorting=False):
195
   def getChildrenOfType(self, plNodeTypeList, poKeySortingMethod=None, pbDoReverseSorting=False):
212
     """return all children nodes of type 'data' or 'node' or 'folder'"""
196
     """return all children nodes of type 'data' or 'node' or 'folder'"""
230
   def getChildNb(self):
214
   def getChildNb(self):
231
     return self.getChildNbOfType([PBNodeType.Data])
215
     return self.getChildNbOfType([PBNodeType.Data])
232
 
216
 
233
-  def getChildren(self):
217
+  def getChildren(self, pbIncludeDeleted=False):
234
     """return all children nodes of type 'data' or 'node' or 'folder'"""
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
   def getContacts(self):
227
   def getContacts(self):
238
     """return all children nodes of type 'data' or 'node' or 'folder'"""
228
     """return all children nodes of type 'data' or 'node' or 'folder'"""
343
           break
333
           break
344
       return PBNodeStatus.getStatusItem(lsRealStatusId)
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
     liMaxLength = int(piCharNb)
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
     return lsTruncatedLabel
353
     return lsTruncatedLabel
354
 
354
 
355
   def getTruncatedContentAsText(self, piCharNb):
355
   def getTruncatedContentAsText(self, piCharNb):
387
     # Does not match @@ at end of content.
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 View File

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>