Browse Source

add support for dynamic view of groups and rights

Damien Accorsi 10 years ago
parent
commit
a0bf661568

+ 14 - 0
pboard/pboard/controllers/api.py View File

@@ -49,9 +49,23 @@ class PODPublicApiController(BaseController):
49 49
         loNewAccount.email_address = email
50 50
         loNewAccount.display_name  = email
51 51
         loNewAccount.password      = password
52
+
52 53
         loUserGroup = pld.PODStaticController.getGroup('user')
53 54
         loUserGroup.users.append(loNewAccount)
55
+
56
+        pm.DBSession.add(loNewAccount)
54 57
         pm.DBSession.flush()
58
+        pm.DBSession.refresh(loNewAccount)
59
+
60
+        loUserSpecificGroup = pld.PODStaticController.createGroup()
61
+
62
+        loUserSpecificGroup.group_id = 0-loNewAccount.user_id # group id of a given user is the opposite of the user id
63
+        loUserSpecificGroup.group_name = ''
64
+        loUserSpecificGroup.personnal_group = True
65
+        loUserSpecificGroup.users.append(loNewAccount)
66
+
67
+        pm.DBSession.flush()
68
+
55 69
         flash(_('Account successfully created: %s') % (email), 'info')
56 70
         redirect(lurl('/'))
57 71
 

+ 3 - 1
pboard/pboard/controllers/root.py View File

@@ -147,7 +147,9 @@ class RootController(BaseController):
147 147
             root_node_list=loRootNodeList,
148 148
             current_node=loCurrentNode,
149 149
             node_status_list = loNodeStatusList,
150
-            keywords = highlight
150
+            keywords = highlight,
151
+            user_specific_groups = pld.PODStaticController.getUserSpecificGroups(),
152
+            real_groups = pld.PODStaticController.getRealGroups()
151 153
         )
152 154
 
153 155
     @expose('pboard.templates.search')

+ 13 - 11
pboard/pboard/lib/auth.py View File

@@ -11,14 +11,16 @@ class can_read(Predicate):
11 11
         pass
12 12
 
13 13
     def evaluate(self, environ, credentials):
14
-        node_id = environ['webob.adhoc_attrs']['validation']['values']['node']
15
-        has_right = session.execute("""
16
-                select *
17
-                from pod_group_node pgn
18
-                join pod_user_group pug on pug.group_id = pgn.group_id
19
-                join pod_user pu on pug.user_id = pu.user_id
20
-                where rights > 0
21
-                and email_address = :mail
22
-                and node_id = :node""", {"mail":credentials["repoze.who.userid"], "node":node_id})
23
-        if has_right.rowcount == 0 :
24
-            self.unmet()
14
+        if 'node' in environ['webob.adhoc_attrs']['validation']['values']:
15
+            node_id = environ['webob.adhoc_attrs']['validation']['values']['node']
16
+            if node_id!=0:
17
+                has_right = session.execute("""
18
+                        select *
19
+                        from pod_group_node pgn
20
+                        join pod_user_group pug on pug.group_id = pgn.group_id
21
+                        join pod_user pu on pug.user_id = pu.user_id
22
+                        where rights > 0
23
+                        and email_address = :mail
24
+                        and node_id = :node""", {"mail":credentials["repoze.who.userid"], "node":node_id})
25
+                if has_right.rowcount == 0 :
26
+                    self.unmet()

+ 17 - 0
pboard/pboard/lib/dbapi.py View File

@@ -41,6 +41,23 @@ class PODStaticController(object):
41 41
     loGroup = pbma.Group.by_group_name(psGroupName)
42 42
     return loGroup
43 43
 
44
+  @classmethod
45
+  def createGroup(cls):
46
+    loGroup = pbma.Group()
47
+    return loGroup
48
+
49
+  @classmethod
50
+  def getGroups(cls):
51
+    loGroups = pbma.Group.real_groups_first()
52
+    return loGroups
53
+
54
+  @classmethod
55
+  def getUserSpecificGroups(cls):
56
+    return DBSession.query(pbma.Group).filter(pbma.Group.personnal_group==True).all()
57
+
58
+  @classmethod
59
+  def getRealGroups(cls):
60
+    return DBSession.query(pbma.Group).filter(pbma.Group.personnal_group==False).all()
44 61
 
45 62
 class PODUserFilteredApiController(object):
46 63
   

+ 33 - 13
pboard/pboard/model/auth.py View File

@@ -11,11 +11,12 @@ though.
11 11
 import os
12 12
 from datetime import datetime
13 13
 from hashlib import sha256
14
+
14 15
 __all__ = ['User', 'Group', 'Permission']
15 16
 
16 17
 from sqlalchemy import Table, ForeignKey, Column
17 18
 from sqlalchemy.types import Unicode, Integer, DateTime, Boolean
18
-from sqlalchemy.orm import relation, synonym
19
+from sqlalchemy.orm import relation, relationship, synonym
19 20
 
20 21
 from pboard.model import DeclarativeBase, metadata, DBSession
21 22
 
@@ -38,13 +39,25 @@ user_group_table = Table('pod_user_group', metadata,
38 39
 )
39 40
 
40 41
 
41
-class Group(DeclarativeBase):
42
-    """
43
-    Group definition
44 42
 
45
-    Only the ``group_name`` column is required.
46 43
 
47
-    """
44
+class Rights(DeclarativeBase):
45
+    READ_ACCESS = 1
46
+    WRITE_ACCESS = 2
47
+
48
+    __tablename__ = 'pod_group_node'
49
+
50
+    group_id = Column(Integer, ForeignKey('pod_group.group_id'), autoincrement=True, primary_key=True)
51
+    node_id = Column(Integer, ForeignKey('pod_nodes.node_id'), autoincrement=True, primary_key=True)
52
+    rights = Column(Integer)
53
+
54
+    def hasReadAccess(self):
55
+        return self.rights & Rights.READ_ACCESS
56
+
57
+    def hasWriteAccess(self):
58
+        return self.rights & Rights.WRITE_ACCESS
59
+
60
+class Group(DeclarativeBase):
48 61
 
49 62
     __tablename__ = 'pod_group'
50 63
 
@@ -55,6 +68,9 @@ class Group(DeclarativeBase):
55 68
     personnal_group = Column(Boolean)
56 69
     users = relation('User', secondary=user_group_table, backref='groups')
57 70
 
71
+    users = relation('User', secondary=user_group_table, backref='groups')
72
+    _lRights = relationship('Rights', remote_side=[Rights.group_id], backref='_oGroup')
73
+
58 74
     def __repr__(self):
59 75
         return '<Group: name=%s>' % repr(self.group_name)
60 76
 
@@ -66,6 +82,17 @@ class Group(DeclarativeBase):
66 82
         """Return the user object whose email address is ``email``."""
67 83
         return DBSession.query(cls).filter_by(group_name=group_name).first()
68 84
 
85
+    def getDisplayName(self) -> str:
86
+        if self.group_id<0:
87
+            return self.users[0].display_name
88
+
89
+        return self.display_name
90
+
91
+    @property
92
+    def rights(self):
93
+        return self._lRights
94
+
95
+
69 96
 class User(DeclarativeBase):
70 97
     """
71 98
     User definition.
@@ -154,14 +181,7 @@ class User(DeclarativeBase):
154 181
         hash.update((password + self.password[:64]).encode('utf-8'))
155 182
         return self.password[64:] == hash.hexdigest()
156 183
 
157
-class Rights(DeclarativeBase):
158
-    __tablename__ = 'pod_group_node'
159
-
160
-    group_id = Column(Integer, ForeignKey('pod_group.group_id'), autoincrement=True, primary_key=True)
161
-    node_id = Column(Integer, ForeignKey('pod_nodes.node_id'), autoincrement=True, primary_key=True)
162
-    rights = Column(Integer)
163 184
 
164
-    groups = relation('PBNode')
165 185
 
166 186
 class Permission(DeclarativeBase):
167 187
     """

+ 1 - 34
pboard/pboard/model/data.py View File

@@ -21,40 +21,6 @@ import tg
21 21
 from pboard.model import DeclarativeBase, metadata, DBSession
22 22
 from pboard.model import auth as pma
23 23
 
24
-# This is the association table for the many-to-many relationship between
25
-# groups and permissions.
26
-"""pod_node_table = Table('pod_nodes', metadata,
27
-    Column('node_id', Integer, Sequence('pod_nodes__node_id__sequence'), primary_key=True),
28
-    Column('parent_id', Integer, ForeignKey('pod_nodes.node_id'), nullable=True, default=None),
29
-    Column('node_order', Integer, nullable=True, default=1),
30
-    Column('node_type',   Unicode(16), unique=False, nullable=False, default='data'),
31
-    Column('node_status', Unicode(16), unique=False, nullable=False, default='new'),
32
-
33
-    Column('created_at', DateTime, unique=False, nullable=False),
34
-    Column('updated_at', DateTime, unique=False, nullable=False),
35
-
36
-    Column('data_label',   Unicode(1024), unique=False, nullable=False, default=''),
37
-    Column('data_content', Text(), unique=False, nullable=False, default=''),
38
-    Column('data_datetime', DateTime, unique=False, nullable=False),
39
-    Column('data_reminder_datetime', DateTime, unique=False, nullable=True),
40
-    
41
-    Column('data_file_name', Unicode(255), unique=False, nullable=False, default=''),
42
-    Column('data_file_mime_type', Unicode(255), unique=False, nullable=False, default=''),
43
-    Column('data_file_content', LargeBinary(), unique=False, nullable=False, default=None),
44
-)
45
-"""
46
-"""
47
-- node_type
48
-
49
-- node_created_at
50
-- node_updated_at
51
-
52
-- data_label
53
-- data_content
54
-- data_source_url
55
-- data_status_id
56
-"""
57
-
58 24
 
59 25
 class PBNodeStatusItem(object):
60 26
   def __init__(self, psStatusId, psStatusLabel, psStatusFamily, psIconId, psCssClass): #, psBackgroundColor):
@@ -228,6 +194,7 @@ class PBNode(DeclarativeBase):
228 194
   _oParent = relationship('PBNode', remote_side=[node_id], backref='_lAllChildren')
229 195
   _oOwner = relationship('User', remote_side=[pma.User.user_id], backref='_lAllNodes')
230 196
 
197
+
231 198
   def getChildrenOfType(self, plNodeTypeList, poKeySortingMethod=None, pbDoReverseSorting=False):
232 199
     """return all children nodes of type 'data' or 'node' or 'folder'"""
233 200
     llChildren = []

+ 42 - 27
pboard/pboard/templates/document-widgets-tabs.mak View File

@@ -45,32 +45,46 @@
45 45
           <th></th>
46 46
         </tr>
47 47
       </thead>
48
-      <tr>
49
-        <td>Recherche et Développement</td>
50
-        <td>
51
-          <span class="label label-success" title="${_('Read access')}">R</span>
52
-          <span class="label label-warning" title="${_('Write access')}">W</span>
53
-        </td>
54
-      </tr>
48
+      % for loCurrentGroup in real_groups:
49
+        <tr>
50
+          <td>${loCurrentGroup.getDisplayName()}</td>
51
+          <td>
52
+            % for loRight in loCurrentGroup.rights:
53
+              % if loRight.node_id==poNode.node_id:
54
+                % if loRight.hasReadAccess():
55
+                  <span class="label label-success">R</span>
56
+                % endif
57
+                % if loRight.hasWriteAccess():
58
+                  <span class="label label-warning">W</span>
59
+                % endif
60
+              % endif
61
+            % endfor
62
+          </td>
63
+        </tr>
64
+      % endfor
55 65
       <thead>
56 66
         <tr>
57 67
           <th><i class="fa fa-user"></i> ${_('Users')}</th>
58 68
           <th></th>
59 69
         </tr>
60 70
       </thead>
61
-      <tr>
62
-        <td>Damien Accorsi</td>
63
-        <td>
64
-          <span class="label label-success">R</span>
65
-        </td>
66
-      </tr>
67
-      <tr>
68
-        <td>Sylvain Ferot</td>
69
-        <td>
70
-          <span class="label label-success">R</span>
71
-          <span class="label label-warning">W</span>
72
-        </td>
73
-      </tr>
71
+      % for loCurrentGroup in user_specific_groups:
72
+        <tr>
73
+          <td>${loCurrentGroup.getDisplayName()}</td>
74
+          <td>
75
+            % for loRight in loCurrentGroup.rights:
76
+              % if loRight.node_id==poNode.node_id:
77
+                % if loRight.hasReadAccess():
78
+                  <span class="label label-success">R</span>
79
+                % endif
80
+                % if loRight.hasWriteAccess():
81
+                  <span class="label label-warning">W</span>
82
+                % endif
83
+              % endif
84
+            % endfor
85
+          </td>
86
+        </tr>
87
+      % endfor
74 88
     </table>
75 89
     
76 90
     % endif
@@ -160,26 +174,26 @@
160 174
     ##
161 175
     ## FIXME - SET A DYNAMIC SELECT LIST HERE
162 176
     ##
163
-              % for loCurrentUser in ((3, 'Research and Development'), (4, 'Sylvain Ferot'), (5, 'Damien Accorsi')):
164
-              <tr id='user-${loCurrentUser[0]}-rights-row'>
177
+              % for loCurrentGroup in user_specific_groups:
178
+              <tr id='user-${loCurrentGroup.group_id}-rights-row'>
165 179
                 <td>
166 180
                   <a
167 181
                     class="btn btn-mini"
168
-                    onclick="updateRights(${loCurrentUser[0]})"
182
+                    onclick="updateRights(${loCurrentGroup.group_id})"
169 183
                   >
170 184
                     <i class="fa fa-key"></i>
171 185
                   </a>
172 186
                 </td>
173 187
                 <td class='pod-highlightable-access-management-cell'>
174
-                  ${loCurrentUser[1]}
188
+                  ${loCurrentGroup.getDisplayName()}
175 189
                   <input
176 190
                     type="hidden"
177
-                    id="user-${loCurrentUser[0]}-value"
178
-                    name="user[${loCurrentUser[0]}]"
191
+                    id="user-${loCurrentGroup.group_id}-value"
192
+                    name="user[${loCurrentGroup.group_id}]"
179 193
                     value=""
180 194
                   />
181 195
                 </td>
182
-                <td id="user-${loCurrentUser[0]}-rights" class="pod-right-cell"></td>
196
+                <td id="user-${loCurrentGroup.group_id}-rights" class="pod-right-cell"></td>
183 197
               </tr>
184 198
               % endfor
185 199
             </table>
@@ -325,6 +339,7 @@
325 339
           >
326 340
             ${loFile.getTruncatedLabel(50)}
327 341
           </a>
342
+          ## FIXME SHOW IMAGE THUMBNAIL <img src="${tg.url('/api/get_file_content_thumbnail/%i'%loFile.node_id)}"/>
328 343
           <a
329 344
             class="pull-right"
330 345
             href="${tg.url('/api/get_file_content/%s'%(loFile.node_id))}"

+ 514 - 0
pboard/pboard/templates/document-widgets-tabs.mak~ View File

@@ -0,0 +1,514 @@
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="HistoryTabContent(poNode)">
6
+  <h4>History</h4>
7
+  <ul>
8
+  % for version in poNode.getHistory():
9
+  	<li><a href="${tg.url('/document/%i/%i'%(version.node_id, version.version_id))}">${version.created_at.strftime("%a %x %X")}</a></li>
10
+  % endfor
11
+  </ul>
12
+</%def>
13
+
14
+<%def name="AccessManagementTab(poNode)">
15
+  ######
16
+  ##
17
+  ## THIS WIDGET IS INTENDED TO BE USED ONE TIME ONLY IN A PAGE
18
+  ##
19
+  <h4>${_('Share options')}</h4> 
20
+  <p>
21
+    This document is
22
+    % if poNode.is_shared==False:
23
+      <span class="label label-info">
24
+        <i class="fa fa-user"></i>
25
+        ${_('private')}
26
+      </span>
27
+    % else:
28
+      <span class="label label-info">
29
+        <i class="fa fa-group"></i>
30
+        ${_('collaborative')}
31
+      </span>
32
+    % endif
33
+  </p>
34
+  <p>
35
+    % if poNode.is_shared==True or poNode.is_shared==False:
36
+      ${_('People working on it are:')}
37
+######
38
+##
39
+## FIXME - SHOW LIST OF GROUPS ALLOWED TO WORK ON THE DOCUMENT
40
+##
41
+    <table class="table table-striped table-hover table-condensed">
42
+      <thead>
43
+        <tr>
44
+          <th><i class="fa fa-group"></i> ${_('Groups')}</th>
45
+          <th></th>
46
+        </tr>
47
+      </thead>
48
+      % for loCurrentGroup in real_groups:
49
+        <tr>
50
+          <td>${loCurrentGroup.getDisplayName()}</td>
51
+          <td>
52
+            % for loRight in loCurrentGroup.rights:
53
+              % if loRight.node_id==poNode.node_id:
54
+                % if loRight.hasReadAccess():
55
+                  <span class="label label-success">R</span>
56
+                % endif
57
+                % if loRight.hasWriteAccess():
58
+                  <span class="label label-warning">W</span>
59
+                % endif
60
+              % endif
61
+            % endfor
62
+          </td>
63
+        </tr>
64
+      % endfor
65
+      <tr>
66
+        <td>Recherche et Développement</td>
67
+        <td>
68
+          <span class="label label-success" title="${_('Read access')}">R</span>
69
+          <span class="label label-warning" title="${_('Write access')}">W</span>
70
+        </td>
71
+      </tr>
72
+      <thead>
73
+        <tr>
74
+          <th><i class="fa fa-user"></i> ${_('Users')}</th>
75
+          <th></th>
76
+        </tr>
77
+      </thead>
78
+      % for loCurrentGroup in user_specific_groups:
79
+        <tr>
80
+          <td>${loCurrentGroup.getDisplayName()}</td>
81
+          <td>
82
+            % for loRight in loCurrentGroup.rights:
83
+              % if loRight.node_id==poNode.node_id:
84
+                % if loRight.hasReadAccess():
85
+                  <span class="label label-success">R</span>
86
+                % endif
87
+                % if loRight.hasWriteAccess():
88
+                  <span class="label label-warning">W</span>
89
+                % endif
90
+              % endif
91
+            % endfor
92
+          </td>
93
+        </tr>
94
+      % endfor
95
+    </table>
96
+    
97
+    % endif
98
+  <p>
99
+
100
+######
101
+##
102
+## 2014-05-06 - D.A. We do not share documents on internet yet.
103
+##
104
+##  <p>
105
+##    % if poNode.is_public==False:
106
+##      ${_('This document is not shared on internet')|n}
107
+##    % else:
108
+##      ${_('This document is <span class="label label-warning"><i class="fa fa-globe"></i><span>shared</span></span> on internet')|n}.
109
+##      ${_('The associated url is:')} <a href="FIXME">${poNode.public_url_key}</a>
110
+##    % endif
111
+##  </p>
112
+  <!-- Button to trigger modal -->
113
+  <a href="#edit-document-share-properties" role="button" class="btn btn-success" data-toggle="modal">
114
+    <i class="fa fa-edit"></i>
115
+    ${_('Edit share options')}
116
+  </a>
117
+     
118
+  <!-- Modal -->
119
+  <div
120
+    id="edit-document-share-properties"
121
+    class="modal hide"
122
+    tabindex="-1"
123
+    role="dialog"
124
+    aria-labelledby="myModalLabel"
125
+    aria-hidden="true">
126
+    
127
+    <div class="modal-header">
128
+      <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
129
+      <h3 id="myModalLabel">Document sharing options</h3>
130
+    </div>
131
+    <div class="modal-body">
132
+
133
+      <form id='document-share-form' method="GET" action="${tg.url('/api/set_access_management?node_id=%d'%poNode.node_id)}">
134
+        <fieldset>
135
+          <label class="checkbox">
136
+            <input name="is_shared" type="checkbox" id="document-share-selector" ${('', 'checked')[poNode.is_shared]}/>
137
+            ${_('Share document with collaborators.')} <i class="fa fa-group"></i>
138
+          </label>
139
+          <div id="document-share-people-selector">
140
+            <p>
141
+              ${_('Select read and write access for each group or people...')}</p>
142
+            <script>
143
+            function updateRights(psUserId) {
144
+              var ACCESS_NONE = '';
145
+              var ACCESS_READ = 'R';
146
+              var ACCESS_WRITE = 'RW';
147
+              
148
+              var nodeIdForSelectedUser = 'user-'+psUserId+'-value';
149
+              var widget = $('#'+nodeIdForSelectedUser);
150
+              var oldValue = widget.val();
151
+              var newValue = '';
152
+              if(oldValue==ACCESS_NONE) {
153
+                newValue = ACCESS_READ;
154
+                newHtml = '<span class="label label-success">R</span>';
155
+              } else if(oldValue==ACCESS_READ) {
156
+                newValue = ACCESS_WRITE;
157
+                newHtml = '<span class="label label-success">R</span> <span class="label label-warning">W</span>';
158
+              } else if (oldValue==ACCESS_WRITE) {
159
+                newValue = ACCESS_NONE;
160
+                newHtml = '';
161
+              } else {
162
+                newValue = ACCESS_READ;
163
+                newHtml = '<span class="label label-success">R</span>';
164
+              }
165
+              
166
+              widget.val(newValue);
167
+              visibleid = 'user-'+psUserId+'-rights';
168
+              $("#"+visibleid).html(newHtml);
169
+            }
170
+            </script>
171
+            
172
+            <table class="table table-striped table-hover table-condensed">
173
+              <thead>
174
+                <tr>
175
+                  <th></th>
176
+                  <th>${_('Group')}</th>
177
+                  <th>${_('Access')}</th>
178
+                </tr>
179
+              </thead>
180
+    ######
181
+    ##
182
+    ## FIXME - SET A DYNAMIC SELECT LIST HERE
183
+    ##
184
+              % for loCurrentGroup in user_specific_groups:
185
+              <tr id='user-${loCurrentGroup.group_id}-rights-row'>
186
+                <td>
187
+                  <a
188
+                    class="btn btn-mini"
189
+                    onclick="updateRights(${loCurrentGroup.group_id})"
190
+                  >
191
+                    <i class="fa fa-key"></i>
192
+                  </a>
193
+                </td>
194
+                <td class='pod-highlightable-access-management-cell'>
195
+                  ${loCurrentGroup.getDisplayName()}
196
+                  <input
197
+                    type="hidden"
198
+                    id="user-${loCurrentGroup.group_id}-value"
199
+                    name="user[${loCurrentGroup.group_id}]"
200
+                    value=""
201
+                  />
202
+                </td>
203
+                <td id="user-${loCurrentGroup.group_id}-rights" class="pod-right-cell"></td>
204
+              </tr>
205
+              % endfor
206
+            </table>
207
+          </div>
208
+        </fieldset>
209
+######
210
+##
211
+## 2014-05-06 - D.A. The documents are not yet sharable through internet
212
+##
213
+##        <fieldset>
214
+##          <label class="checkbox">
215
+##            <input name="is_public" type="checkbox" id="document-public-selector" ${('', 'checked')[poNode.is_public]}/>
216
+##            ${_('Internet shared document')}
217
+##            <i class="fa fa-globe"></i>
218
+##          </label>
219
+##          <label id="document-public-key-selector">
220
+##            ${_('Key')}
221
+##            <div class="input-append">
222
+##              <input name="url_public_key" id="document-public-key" type="text">
223
+##              <span id="document-public-key-refresh-button" class="add-on btn" title="${_('Regenerate key')}">
224
+##                <i class="fa fa-refresh"></i>
225
+##              </span>
226
+##            </div>
227
+##            <p><a id='document-public-key-url' href="">http://share.pod.com/document/azefnzeioguneriugnreiugnre</a></p>
228
+##          </label>
229
+
230
+        </fieldset>
231
+####
232
+## Button replaced by modal dialog button
233
+##        <button type="submit" class="btn btn-success">
234
+##          <i class="fa fa-check"></i>
235
+##          ${_('Save')}
236
+##        </button>
237
+      </form>
238
+    </div>
239
+    <div class="modal-footer">
240
+    <button class="btn" data-dismiss="modal" aria-hidden="true">
241
+      <i class="fa fa-ban"></i> ${_('Cancel')}
242
+    </button>
243
+    <button class="btn btn-success" id="document-share-form-submit-button">
244
+      <i class="fa fa-check"></i> ${_('Save changes')}
245
+    </button>
246
+    </div>
247
+    <script>
248
+##
249
+## 2014-05-06 - D.A. - Documents are not yet sharable through internet
250
+##
251
+##        function refreshDocumentPublicKey(psNewPublicKey) {
252
+##          var lsNewUrl = 'http://share.pod.com/document/'+psNewPublicKey;
253
+##          $('#document-public-key').val(psNewPublicKey);
254
+##          $('#document-public-key-url').attr('href', lsNewUrl);
255
+##          $('#document-public-key-url').text(lsNewUrl);
256
+##        }
257
+      
258
+      function toggleDocumentSharePeopleSelector(pbShowIt) {
259
+        if (pbShowIt) {
260
+          $('#document-share-people-selector').show();
261
+          // $('#document-share-people-selector input').removeAttr("disabled");
262
+        } else {
263
+          $('#document-share-people-selector').hide();
264
+          // $('#document-share-people-selector input').prop('disabled', 'disabled');
265
+        }
266
+      }
267
+
268
+##
269
+## 2014-05-06 - D.A. - Documents are not yet sharable through internet
270
+##
271
+##        function toggleDocumentPublicKeyGenerator(pbShowIt) {
272
+##          if (pbShowIt) {
273
+##            $('#document-public-key-selector input').removeAttr("disabled");
274
+##            $('#document-public-key-refresh-button').removeProp('disabled');
275
+##            $('#document-public-key-refresh-button').removeClass('btn-disabled');
276
+##            $('#document-public-key-selector a').show();
277
+##            $('#document-public-key-refresh-button').on("click").click(function () {
278
+##              refreshDocumentPublicKey(generateStringId()); // New random 32-char id
279
+##            });
280
+##            if($('#document-public-key-selector input').val()=='') {
281
+##              refreshDocumentPublicKey(generateStringId());
282
+##            }
283
+##          } else {
284
+##            $('#document-public-key-refresh-button').prop('disabled', true);
285
+##            $('#document-public-key-refresh-button').addClass('btn-disabled');
286
+##            $('#document-public-key-selector input').prop('disabled', 'disabled');
287
+##            $('#document-public-key-refresh-button').off("click");
288
+##            $('#document-public-key-selector a').hide();
289
+##          }
290
+##        }
291
+##
292
+##
293
+##
294
+
295
+      // Callbacks setup
296
+      $('#document-share-selector').change(function () {
297
+        var checkedValue = $('#document-share-selector').prop("checked");
298
+        toggleDocumentSharePeopleSelector(checkedValue);
299
+      });
300
+
301
+##        $('#document-public-selector').change(function () {
302
+##          var checkedValue = $('#document-public-selector').prop("checked");
303
+##          toggleDocumentPublicKeyGenerator(checkedValue);
304
+##        });
305
+
306
+      // Submit access-management modal dialog form
307
+      $('#document-share-form-submit-button').click(function(){
308
+        $('#document-share-form')[0].submit();
309
+      });
310
+
311
+      // Initial setup
312
+      // Activate or disactivate users selector according
313
+      // to current state of the is_shared property
314
+      //
315
+      // FIXME - 2014-05-06 - This is not working (should be done at document.ready time)
316
+      // note: putting this in a document.ready callback does not work.
317
+      //
318
+      $('#document-share-form')[0].reset();
319
+      toggleDocumentSharePeopleSelector($('#document-share-selector').prop("checked"));
320
+##        toggleDocumentPublicKeyGenerator($('#document-public-selector').prop("checked"));  
321
+##        
322
+##        refreshDocumentPublicKey($('#document-public-key').val()); // First init
323
+
324
+    </script>
325
+  </div>
326
+</%def>
327
+
328
+<%def name="FileTabContent(poNode)">
329
+  <h4>${_('Attachments')}</h4>
330
+  
331
+  % if len(poNode.getFiles())<=0:
332
+    <p class="pod-grey">${_("There is currently no attachment.")}<br/></p>
333
+    <p>${POD.OpenModalButton(h.ID.AddFileModalForm(poNode), _(' Attach first file'))}</p>
334
+  % else:
335
+    <p>${POD.OpenModalButton(h.ID.AddFileModalForm(poNode), _(' Attach a file'))}</p>
336
+  % endif
337
+
338
+  <div>
339
+    % if len(poNode.getFiles())>0:
340
+      % for loFile in poNode.getFiles():
341
+        <p style="list-style-type:none; margin-bottom: 0.5em;">
342
+          <i class="fa fa-paperclip"></i>
343
+          <a
344
+            href="${tg.url('/document/%i'%loFile.node_id)}"
345
+            title="${_('View the attachment')}: ${loFile.getTruncatedLabel(-1)}"
346
+          >
347
+            ${loFile.getTruncatedLabel(50)}
348
+          </a>
349
+          ## FIXME SHOW IMAGE THUMBNAIL <img src="${tg.url('/api/get_file_content_thumbnail/%i'%loFile.node_id)}"/>
350
+          <a
351
+            class="pull-right"
352
+            href="${tg.url('/api/get_file_content/%s'%(loFile.node_id))}"
353
+            title="${_('View the attachment')}"
354
+          >
355
+            <i class="fa fa-download"></i>
356
+          </a>
357
+        </p>
358
+      % endfor
359
+    % endif
360
+  </div>
361
+</%def>
362
+
363
+<%def name="SubdocumentContent(poNode)">
364
+  <h4>${_('Sub-documents')}</h4>
365
+  
366
+  % if len(poNode.getChildren())<=0:
367
+    <p class="pod-grey">${_("There is currently no child documents.")}</p>
368
+  % endif
369
+  <p>${POD.OpenModalButton(h.ID.AddDocumentModalForm(poNode), _('Add a document'))}</p>
370
+
371
+  % if len(poNode.getChildren())>0:
372
+    <div>
373
+      % for subnode in poNode.getChildren():
374
+        <p style="list-style-type:none;">
375
+          <i class="fa-fw ${subnode.getIconClass()}"></i>
376
+            <a href="${tg.url('/document/%i'%subnode.node_id)}">
377
+              ${subnode.data_label}
378
+            </a>
379
+        </p>
380
+      % endfor
381
+    </div>
382
+  % endif
383
+</%def>
384
+
385
+<%def name="EventTabContent(poNode)">
386
+  <h4>${_('Calendar')}</h4>
387
+  
388
+  % if len(poNode.getEvents())<=0:
389
+    <p class="pod-grey">${_("The calendar is empty.")}<br/></p>
390
+    <p>${POD.OpenModalButton(h.ID.AddEventModalForm(poNode), _(' Add first event'))}</p>
391
+  % else:
392
+    <p>${POD.OpenModalButton(h.ID.AddEventModalForm(poNode), _(' Add an event'))}</p>
393
+  % endif
394
+
395
+  % if len(poNode.getEvents())>0:
396
+    <table class="table table-striped table-hover table-condensed">
397
+      <thead>
398
+        <tr>
399
+          <th>Date</th>
400
+          <th>Time</th>
401
+          <th>
402
+            Event
403
+          </th>
404
+          <th>
405
+            <a href="" title="Add an event"><i class="icon-g-plus"></i></a>
406
+          </th>
407
+        </tr>
408
+      </thead>
409
+      % for event in poNode.getEvents():
410
+        <tr class="item-with-data-popoverable" data-content="${event.data_content}" rel="popover" data-placement="left" data-trigger="hover">
411
+          <td>${event.getFormattedDate(event.data_datetime)}</td>
412
+          <td>${event.getFormattedTime(event.data_datetime)}</td>
413
+          <td>${event.data_label}</td>
414
+        </tr>
415
+  ## FIXME                    <script>
416
+  ##                      $('.item-with-data-popoverable').popover({ html: true});
417
+  ##                    </script>
418
+
419
+      % endfor
420
+    </table>
421
+  % endif
422
+</%def>
423
+
424
+<%def name="ContactTabContent(poNode)">
425
+  <h4>${_('Address book')}</h4> 
426
+  % if len(poNode.getContacts())<=0:
427
+    <p class="pod-grey">${_("The address book is empty.")}<br/></p>
428
+    <p>${POD.OpenModalButton(h.ID.AddContactModalForm(poNode), _('Add first contact'))}</p>
429
+  % else:
430
+    <p>${POD.OpenModalButton(h.ID.AddContactModalForm(poNode), _('Add a contact'))}</p>
431
+  % endif
432
+
433
+  <!-- LIST OF CONTACT NODES -->
434
+  % for contact in poNode.getContacts():
435
+    <div class="well">
436
+      <legend class="text-info">
437
+        ${contact.data_label}
438
+        ## TODO - 2013-11-20 - Use the right form in order to update meta-data
439
+        <a class="pull-right" href="${tg.url('/document/%i'%contact.node_id)}"><i class="fa fa-edit"></i></a>
440
+      </legend>
441
+      
442
+      <div>
443
+        ## FIXME - D.A. - 2013-11-15 - Implement localisation stuff <a style='float: right;' href="" title='${_('Search on google maps')}'><i class='icon-g-google-maps'></i></a>
444
+        ${contact.data_content|n}
445
+      </div>
446
+    </div>
447
+  % endfor
448
+</%def>
449
+
450
+<%def name="CommentTabContent(poNode)">
451
+  <h4>${_('Comment thread')}</h4>
452
+  
453
+  % if len(poNode.getComments())<=0:
454
+    <p class="pod-grey">${_("The comment thread is empty.")}<br/></p>
455
+  % endif
456
+
457
+  % if len(poNode.getComments())>0:
458
+    % if len(poNode.getComments())>5:
459
+      ##
460
+      ## We show a "direct down" button in case the page is too long
461
+      ##
462
+      <p>${POD.OpenLinkButton(h.ID.AddCommentInlineForm(), _('Add a comment'))}</p>
463
+    % endif
464
+    <div>
465
+      % for comment in poNode.getComments():
466
+        <p>
467
+          <a href="${tg.url('/api/toggle_share_status', dict(node_id=comment.node_id))}">
468
+            % if comment.is_shared:
469
+              <span class="label label-warning" title="${_('Shared comment. Click to make private.')}">${h.ICON.Shared|n}</span>
470
+            % else:
471
+              <span class="label label-info" title="${_('Private comment. Click to share.')}">${h.ICON.Private|n}</span>
472
+            % endif
473
+          </a>
474
+          <strong>${comment._oOwner.display_name}</strong>
475
+          <i class="pull-right">
476
+            The ${comment.getFormattedDate(comment.updated_at)} 
477
+            at ${comment.getFormattedTime(comment.updated_at)}
478
+          </i>
479
+          <br/>
480
+          ${comment.data_content|n}
481
+          <hr style="border-top: 1px dotted #ccc; margin: 0;"/>
482
+        </p>
483
+      % endfor
484
+    </div>
485
+  % endif
486
+
487
+  <form class="form" id="${h.ID.AddCommentInlineForm()}" action="${tg.url('/api/create_comment')}" method="POST">
488
+    <input type="hidden" name='parent_id' value='${poNode.node_id}'/>
489
+    <input type="hidden" name='data_label' value=""/>
490
+    <input type="hidden" id="add_comment_data_content_textarea" name='data_content' />
491
+    <label>
492
+      ${_('Write your comment below:')}
493
+      ${POD.RichTextEditor('add_comment_data_content_textarea_wysiwyg', '', 'boldanditalic')}
494
+    </label>
495
+    <label>
496
+      <input type="checkbox" name='is_shared'/> ${_('Share this comment')}
497
+    </label>
498
+    <span class="pull-right">
499
+      % if len(poNode.getComments())<=0:
500
+        ${POD.SaveButton('current-document-add-comment-save-button', True, _('Add first comment'))}
501
+      % else:
502
+        ${POD.SaveButton('current-document-add-comment-save-button', True, _('Comment'))}
503
+      % endif
504
+    </span>
505
+  </form>
506
+  <script>
507
+      $('#current-document-add-comment-save-button').on('click', function(e){
508
+      e.preventDefault(); // We don't want this to act as a link so cancel the link action
509
+      $('#add_comment_data_content_textarea_wysiwyg').cleanHtml();
510
+      $('#add_comment_data_content_textarea').val($('#add_comment_data_content_textarea_wysiwyg').html());
511
+      $('#current-document-add-comment-form').submit();
512
+    });
513
+  </script>
514
+</%def>