Browse Source

add user-based filtering of data

damien 11 years ago
parent
commit
0edcaa2960

+ 96 - 25
pboard/pboard/controllers/api.py View File

16
 from pboard.model import data as pmd
16
 from pboard.model import data as pmd
17
 from pboard import model as pm
17
 from pboard import model as pm
18
 
18
 
19
-__all__ = ['PODApiController']
19
+__all__ = ['PODPublicApiController', 'PODApiController']
20
+
21
+class PODPublicApiController(BaseController):
22
+
23
+    @expose('pboard.templates.index')
24
+    def index(self):
25
+        """Let the user know that's visiting a protected controller."""
26
+        flash(_("Secure Controller here"))
27
+        return dict(page='index')
28
+    
29
+    @expose()
30
+    def create_account(self, email=u'', password=u'', retyped_password=u'', **kw):
31
+      if email==u'' or password==u'' or retyped_password==u'':
32
+        flash(_('Account creation error: please fill all the fields'), 'error')
33
+        # redirect(lurl('/'))
34
+      elif password!=retyped_password:
35
+        flash(_('Account creation error: passwords do not match'), 'error')
36
+        # redirect(lurl('/'))
37
+      else:
38
+        loExistingUser = pld.PODStaticController.getUserByEmailAddress(email)
39
+        if loExistingUser!=None:
40
+          flash(_('Account creation error: account already exist: %s') % (email), 'error')
41
+          # redirect(lurl('/'))
42
+        
43
+        loNewAccount = pld.PODStaticController.createUser()
44
+        loNewAccount.email_address = email
45
+        loNewAccount.display_name  = email
46
+        loNewAccount.password      = password
47
+        loUserGroup = pld.PODStaticController.getGroup('user')
48
+        loUserGroup.users.append(loNewAccount)
49
+        pm.DBSession.flush()
50
+        flash(_('Account successfully created: %s') % (email), 'info')
51
+        # redirect(lurl('/'))
52
+
20
 
53
 
21
 class PODApiController(BaseController):
54
 class PODApiController(BaseController):
22
     """Sample controller-wide authorization"""
55
     """Sample controller-wide authorization"""
33
     
66
     
34
     @expose()
67
     @expose()
35
     def create_event(self, parent_id=None, data_label=u'', data_datetime=None, data_content=u'', data_reminder_datetime=None, add_reminder=False, **kw):
68
     def create_event(self, parent_id=None, data_label=u'', data_datetime=None, data_content=u'', data_reminder_datetime=None, add_reminder=False, **kw):
36
-
37
-      loNewNode = pld.createNode()
69
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
70
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
71
+      
72
+      loNewNode = loApiController.createNode()
38
       loNewNode.parent_id     = int(parent_id)
73
       loNewNode.parent_id     = int(parent_id)
39
       loNewNode.node_type     = pmd.PBNodeType.Event
74
       loNewNode.node_type     = pmd.PBNodeType.Event
40
       loNewNode.data_label    = data_label
75
       loNewNode.data_label    = data_label
48
 
83
 
49
     @expose()
84
     @expose()
50
     def create_contact(self, parent_id=None, data_label=u'', data_content=u'', **kw):
85
     def create_contact(self, parent_id=None, data_label=u'', data_content=u'', **kw):
51
-
52
-      loNewNode = pld.createNode()
86
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
87
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
88
+      
89
+      loNewNode = loApiController.createNode()
53
       loNewNode.parent_id     = int(parent_id)
90
       loNewNode.parent_id     = int(parent_id)
54
       loNewNode.node_type     = pmd.PBNodeType.Contact
91
       loNewNode.node_type     = pmd.PBNodeType.Contact
55
       loNewNode.data_label    = data_label
92
       loNewNode.data_label    = data_label
60
 
97
 
61
     @expose()
98
     @expose()
62
     def create_comment(self, parent_id=None, data_label=u'', data_content=u'', **kw):
99
     def create_comment(self, parent_id=None, data_label=u'', data_content=u'', **kw):
63
-
64
-      loNewNode = pld.createNode()
100
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
101
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
102
+      
103
+      loNewNode = loApiController.createNode()
65
       loNewNode.parent_id     = int(parent_id)
104
       loNewNode.parent_id     = int(parent_id)
66
       loNewNode.node_type     = pmd.PBNodeType.Comment
105
       loNewNode.node_type     = pmd.PBNodeType.Comment
67
       loNewNode.data_label    = data_label
106
       loNewNode.data_label    = data_label
72
 
111
 
73
     @expose()
112
     @expose()
74
     def create_file(self, parent_id=None, data_label=u'', data_content=u'', data_file=None, **kw):
113
     def create_file(self, parent_id=None, data_label=u'', data_content=u'', data_file=None, **kw):
75
-
76
-      loNewNode = pld.createNode()
114
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
115
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
116
+      
117
+      loNewNode = loApiController.createNode()
77
       loNewNode.parent_id     = int(parent_id)
118
       loNewNode.parent_id     = int(parent_id)
78
       loNewNode.node_type     = pmd.PBNodeType.File
119
       loNewNode.node_type     = pmd.PBNodeType.File
79
       loNewNode.data_label    = data_label
120
       loNewNode.data_label    = data_label
91
       if node_id==None:
132
       if node_id==None:
92
         return
133
         return
93
       else:
134
       else:
94
-        loFile = pld.getNode(node_id)
135
+        loCurrentUser   = pld.PODStaticController.getCurrentUser()
136
+        loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
137
+        
138
+        loFile = loApiController.getNode(node_id)
95
         tg.response.headers['Content-type'] = str(loFile.data_file_mime_type)
139
         tg.response.headers['Content-type'] = str(loFile.data_file_mime_type)
96
         return loFile.data_file_content
140
         return loFile.data_file_content
97
 
141
 
100
       if node_id==None:
144
       if node_id==None:
101
         return
145
         return
102
       else:
146
       else:
103
-        loFile = pld.getNode(node_id)
147
+        loCurrentUser   = pld.PODStaticController.getCurrentUser()
148
+        loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
149
+        
150
+        loFile = loApiController.getNode(node_id)
104
         
151
         
105
         loJpegBytes = csio.StringIO(loFile.data_file_content)
152
         loJpegBytes = csio.StringIO(loFile.data_file_content)
106
         loImage     = pil.open(loJpegBytes)
153
         loImage     = pil.open(loJpegBytes)
113
 
160
 
114
     @expose()
161
     @expose()
115
     def set_parent_node(self, node_id, new_parent_id, **kw):
162
     def set_parent_node(self, node_id, new_parent_id, **kw):
116
-      loNewNode = pld.getNode(node_id)
163
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
164
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
165
+      
166
+      # TODO - D.A. - 2013-11-07 - Check that new parent is accessible by the user !!!
167
+      loNewNode = loApiController.getNode(node_id)
117
       if new_parent_id!='':
168
       if new_parent_id!='':
118
         loNewNode.parent_id = int(new_parent_id)
169
         loNewNode.parent_id = int(new_parent_id)
119
       pm.DBSession.flush()
170
       pm.DBSession.flush()
121
 
172
 
122
     @expose()
173
     @expose()
123
     def move_node_upper(self, node_id=0):
174
     def move_node_upper(self, node_id=0):
124
-      loNode = pld.getNode(node_id)
125
-      pld.moveNodeUpper(loNode)
175
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
176
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
177
+      
178
+      loNode = loApiController.getNode(node_id)
179
+      loApiController.moveNodeUpper(loNode)
126
       redirect(lurl('/document/%s'%(node_id)))
180
       redirect(lurl('/document/%s'%(node_id)))
127
 
181
 
128
     @expose()
182
     @expose()
129
     def move_node_lower(self, node_id=0):
183
     def move_node_lower(self, node_id=0):
130
-      loNode = pld.getNode(node_id)
131
-      pld.moveNodeLower(loNode)
184
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
185
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
186
+      
187
+      loNode = loApiController.getNode(node_id)
188
+      loApiController.moveNodeLower(loNode)
132
       redirect(lurl('/document/%s'%(node_id)))
189
       redirect(lurl('/document/%s'%(node_id)))
133
 
190
 
134
     @expose()
191
     @expose()
135
     def create_document(self, parent_id=None):
192
     def create_document(self, parent_id=None):
136
-      loNewNode = pld.createNode()
193
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
194
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
195
+      
196
+      loNewNode = loApiController.createDummyNode()
137
       loNewNode.data_label   = 'New document'
197
       loNewNode.data_label   = 'New document'
138
       loNewNode.data_content = 'insert content...'
198
       loNewNode.data_content = 'insert content...'
139
       if int(parent_id)==0:
199
       if int(parent_id)==0:
146
 
206
 
147
     @expose()
207
     @expose()
148
     def edit_label(self, node_id, data_label):
208
     def edit_label(self, node_id, data_label):
149
-      loNewNode = pld.getNode(node_id)
150
-      loNewNode.data_label   = data_label
209
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
210
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
211
+      
212
+      loNode = loApiController.getNode(node_id)
213
+      loNode.data_label = data_label
151
       redirect(lurl('/document/%s'%(node_id)))
214
       redirect(lurl('/document/%s'%(node_id)))
152
 
215
 
153
     @expose()
216
     @expose()
154
     def edit_status(self, node_id, node_status):
217
     def edit_status(self, node_id, node_status):
155
-      loNewNode = pld.getNode(node_id)
156
-      loNewNode.node_status = node_status
218
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
219
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
220
+      
221
+      loNode = loApiController.getNode(node_id)
222
+      loNode.node_status = node_status
157
       redirect(lurl('/document/%s'%(node_id)))
223
       redirect(lurl('/document/%s'%(node_id)))
158
 
224
 
159
     @expose()
225
     @expose()
160
     def edit_content(self, node_id, data_content, **kw):
226
     def edit_content(self, node_id, data_content, **kw):
161
-      loNewNode = pld.getNode(node_id)
162
-      loNewNode.data_content = data_content
227
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
228
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
229
+      
230
+      loNode = loApiController.getNode(node_id)
231
+      loNode.data_content = data_content
163
       redirect(lurl('/document/%s'%(node_id)))
232
       redirect(lurl('/document/%s'%(node_id)))
164
 
233
 
165
     @expose()
234
     @expose()
166
     def force_delete_node(self, node_id=None):
235
     def force_delete_node(self, node_id=None):
167
-      loNode     = pld.getNode(node_id)
236
+      loCurrentUser   = pld.PODStaticController.getCurrentUser()
237
+      loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
238
+      loNode = loApiController.getNode(node_id)
168
       liParentId = loNode.parent_id
239
       liParentId = loNode.parent_id
169
       if loNode.getChildNb()<=0:
240
       if loNode.getChildNb()<=0:
170
         pm.DBSession.delete(loNode)
241
         pm.DBSession.delete(loNode)
172
 
243
 
173
     @expose()
244
     @expose()
174
     def reindex_nodes(self, back_to_node_id=0):
245
     def reindex_nodes(self, back_to_node_id=0):
175
-      """show the user dashboard"""
246
+      # FIXME - NOT SAFE
176
       loRootNodeList   = pm.DBSession.query(pmd.PBNode).order_by(pmd.PBNode.parent_id).all()
247
       loRootNodeList   = pm.DBSession.query(pmd.PBNode).order_by(pmd.PBNode.parent_id).all()
177
       for loNode in loRootNodeList:
248
       for loNode in loRootNodeList:
178
         if loNode.parent_id==None:
249
         if loNode.parent_id==None:

+ 27 - 6
pboard/pboard/controllers/root.py View File

18
 from pboard.controllers import api as pbca
18
 from pboard.controllers import api as pbca
19
 from pboard.controllers import debug as pbcd
19
 from pboard.controllers import debug as pbcd
20
 
20
 
21
+from pboard import model as pm
22
+
21
 import pboard.model.data as pbmd
23
 import pboard.model.data as pbmd
22
 
24
 
23
 __all__ = ['RootController']
25
 __all__ = ['RootController']
43
     debug = pbcd.DebugController()
45
     debug = pbcd.DebugController()
44
     error = ErrorController()
46
     error = ErrorController()
45
 
47
 
48
+    public_api = pbca.PODPublicApiController()
49
+
46
     def _before(self, *args, **kw):
50
     def _before(self, *args, **kw):
47
         tmpl_context.project_name = "pboard"
51
         tmpl_context.project_name = "pboard"
48
 
52
 
90
     @require(predicates.in_group('user', msg=l_('Please login to access this page')))
94
     @require(predicates.in_group('user', msg=l_('Please login to access this page')))
91
     def document(self, node=0, came_from=lurl('/')):
95
     def document(self, node=0, came_from=lurl('/')):
92
         """show the user dashboard"""
96
         """show the user dashboard"""
93
-        import pboard.model.data as pbmd
94
-        
97
+        loCurrentUser   = pld.PODStaticController.getCurrentUser()
98
+        loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
99
+
95
         # loRootNodeList   = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.parent_id==None).order_by(pbmd.PBNode.node_order).all()
100
         # loRootNodeList   = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.parent_id==None).order_by(pbmd.PBNode.node_order).all()
96
-        loRootNodeList = pld.buildTreeListForMenu()
97
-        liNodeId         = max(int(node), 1) # show node #1 if no selected node
98
-        loCurrentNode    = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.node_id==liNodeId).one()
99
-        loNodeStatusList = pbmd.PBNodeStatus.getList()
101
+        loRootNodeList = loApiController.buildTreeListForMenu()
102
+        liNodeId         = int(node)
103
+        
104
+        loCurrentNode    = None
105
+        loNodeStatusList = None
106
+        try:
107
+          loCurrentNode    = loApiController.getNode(liNodeId)
108
+          loNodeStatusList = pbmd.PBNodeStatus.getList()
109
+        except Exception, e:
110
+          flash(_('Document not found'), 'error')
111
+        
112
+        # FIXME - D.A - 2013-11-07 - Currently, the code build a new item if no item found for current user
113
+        # the correct behavior should be to redirect to setup page
114
+        if loCurrentNode is None:
115
+          loCurrentNode = loApiController.getNode(0) # try to get an item
116
+          if loCurrentNode is None:
117
+            flash(_('Your first document has been automatically created'), 'info')
118
+            loCurrentNode = loApiController.createDummyNode()
119
+            pm.DBSession.flush()
120
+
100
         return dict(root_node_list=loRootNodeList, current_node=loCurrentNode, node_status_list = loNodeStatusList)
121
         return dict(root_node_list=loRootNodeList, current_node=loCurrentNode, node_status_list = loNodeStatusList)
101
 
122
 

+ 142 - 85
pboard/pboard/lib/dbapi.py View File

4
 import os
4
 import os
5
 from datetime import datetime
5
 from datetime import datetime
6
 from hashlib import sha256
6
 from hashlib import sha256
7
-__all__ = ['User', 'Group', 'Permission']
8
 
7
 
9
 from sqlalchemy import Table, ForeignKey, Column
8
 from sqlalchemy import Table, ForeignKey, Column
10
 from sqlalchemy.types import Unicode, Integer, DateTime, Text
9
 from sqlalchemy.types import Unicode, Integer, DateTime, Text
13
 
12
 
14
 from pboard.model import DeclarativeBase, metadata, DBSession
13
 from pboard.model import DeclarativeBase, metadata, DBSession
15
 from pboard.model import data as pbmd
14
 from pboard.model import data as pbmd
15
+from pboard.model import auth as pbma
16
 import pboard.model as pbm
16
 import pboard.model as pbm
17
 
17
 
18
-def createNode():
19
-  loNode = pbmd.PBNode()
20
-  DBSession.add(loNode)
21
-  return loNode
18
+import tg
19
+class PODStaticController(object):
22
 
20
 
23
-def getNode(liNodeId):
24
-  return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.node_id==liNodeId).one()
21
+  @classmethod
22
+  def getCurrentUser(cls):
23
+    loCurrentUser = pbma.User.by_email_address(tg.request.identity['repoze.who.userid'])
24
+    return loCurrentUser
25
 
25
 
26
-def getParentNode(loNode):
27
-  return DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.node_id==loNode.parent_id).one()
28
-
29
-def getSiblingNodes(poNode, pbReverseOrder=False):
30
-  if pbReverseOrder:
31
-    return DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.parent_id==poNode.parent_id).order_by(pbmd.PBNode.node_order.desc()).all()
32
-  else:
33
-    return DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.parent_id==poNode.parent_id).order_by(pbmd.PBNode.node_order).all()
34
-
35
-def resetNodeOrderOfSiblingNodes(loSiblingNodes):
36
-  liNewWeight = 0
37
-  for loNode in loSiblingNodes:
38
-    liNewWeight = liNewWeight + 1
39
-    loNode.node_order = liNewWeight
40
-  # DBSession.save()
41
-
42
-def getNodeFileContent(liNodeId):
43
-  return DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.node_id==liNodeId).one().data_file_content
26
+  @classmethod
27
+  def getUserByEmailAddress(cls, psEmailAddress):
28
+    loUser = pbma.User.by_email_address(psEmailAddress)
29
+    return loUser
30
+  
31
+  @classmethod
32
+  def createUser(cls):
33
+    loUser = pbma.User()
34
+    return loUser
35
+  
36
+  @classmethod
37
+  def getGroup(cls, psGroupName):
38
+    loGroup = pbma.Group.by_group_name(psGroupName)
39
+    return loGroup
44
 
40
 
45
 
41
 
46
-def moveNodeUpper(loNode):
47
-  # FIXME - manage errors and logging
42
+class PODUserFilteredApiController(object):
48
   
43
   
49
-  loSiblingNodes = getSiblingNodes(loNode)
50
-  resetNodeOrderOfSiblingNodes(loSiblingNodes)
51
-
52
-  loPreviousItem = None
53
-  for loItem in loSiblingNodes:
54
-    if loItem==loNode:
55
-      if loPreviousItem==None:
56
-        # FIXME
57
-        print "No previous node"
58
-      else:
59
-        liPreviousItemOrder       = loPreviousItem.node_order
60
-        loPreviousItem.node_order = loNode.node_order
61
-        loNode.node_order         = liPreviousItemOrder
62
-        # DBSession.save()
63
-        break
64
-    loPreviousItem = loItem
65
-
66
-def moveNodeLower(loNode):
67
-  # FIXME - manage errors and logging
44
+  def __init__(self, piUserId, piExtraUserIdList=[]):
45
+    self._iCurrentUserId       = piUserId
46
+    self._iExtraUserIdList     = piExtraUserIdList
47
+    self._iUserIdFilteringList = None
68
   
48
   
69
-  loSiblingNodes = getSiblingNodes(loNode)
70
-  resetNodeOrderOfSiblingNodes(loSiblingNodes)
71
-
72
-  loPreviousItem = None
73
-  for loItem in reversed(loSiblingNodes):
74
-    if loItem==loNode:
75
-      if loPreviousItem==None:
76
-        # FIXME
77
-        print "No previous node"
78
-      else:
79
-        liPreviousItemOrder       = loPreviousItem.node_order
80
-        loPreviousItem.node_order = loNode.node_order
81
-        loNode.node_order         = liPreviousItemOrder
82
-        # DBSession.save()
83
-        break
84
-    loPreviousItem = loItem
85
-
86
-def deleteNode(loNode):
87
-  DBSession.delete(loNode)
88
-  return
89
-
90
-def buildTreeListForMenu():
91
-  loNodeList = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.node_type==pbmd.PBNodeType.Data).order_by(pbmd.PBNode.parent_tree_path).order_by(pbmd.PBNode.node_order).order_by(pbmd.PBNode.node_id).all()
92
-  loTreeList = []
93
-  loTmpDict = {}
94
-  for loNode in loNodeList:
95
-    loTmpDict[loNode.node_id] = loNode
96
-
97
-    if loNode.parent_id==None:
98
-      loTreeList.append(loNode)
49
+  def _getUserIdListForFiltering(self):
50
+    if self._iUserIdFilteringList==None:
51
+      self._iUserIdFilteringList = list()
52
+      self._iUserIdFilteringList.append(self._iCurrentUserId)
53
+      for liUserId in self._iExtraUserIdList:
54
+        self._iUserIdFilteringList.append(liUserId)
55
+    return self._iUserIdFilteringList
56
+
57
+  def createNode(self):
58
+    loNode          = pbmd.PBNode()
59
+    loNode.owner_id = self._iCurrentUserId
60
+    DBSession.add(loNode)
61
+    return loNode
62
+  
63
+    query.filter(User.name.in_(['ed', 'wendy', 'jack']))
64
+
65
+  def createDummyNode(self):
66
+    loNewNode = self.createNode()
67
+    loNewNode.data_label   = 'New document'
68
+    loNewNode.data_content = 'insert content...'
69
+    return loNewNode
70
+
71
+  def getNode(self, liNodeId):
72
+    liOwnerIdList = self._getUserIdListForFiltering()
73
+    if liNodeId==0:
74
+      return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).first()
99
     else:
75
     else:
100
-      # append the node to the parent list
101
-      # FIXME - D.A - 2013-10-08
102
-      # The following line may raise an exception
103
-      # We suppose that the parent node has already been added
104
-      # this *should* be the case, but the code does not check it
105
-      if loTmpDict.has_key(loNode.parent_id)==False:
106
-        loTmpDict[loNode.parent_id] = getNode(loNode.parent_id)
107
-      loTmpDict[loNode.parent_id].appendStaticChild(loNode)
76
+      return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.node_id==liNodeId).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).one()
77
+
78
+  def buildTreeListForMenu(self):
79
+    liOwnerIdList = self._getUserIdListForFiltering()
80
+    
81
+    loNodeList = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_type==pbmd.PBNodeType.Data).order_by(pbmd.PBNode.parent_tree_path).order_by(pbmd.PBNode.node_order).order_by(pbmd.PBNode.node_id).all()
82
+    loTreeList = []
83
+    loTmpDict = {}
84
+    for loNode in loNodeList:
85
+      loTmpDict[loNode.node_id] = loNode
86
+  
87
+      if loNode.parent_id==None:
88
+        loTreeList.append(loNode)
89
+      else:
90
+        # append the node to the parent list
91
+        # FIXME - D.A - 2013-10-08
92
+        # The following line may raise an exception
93
+        # We suppose that the parent node has already been added
94
+        # this *should* be the case, but the code does not check it
95
+        if loTmpDict.has_key(loNode.parent_id)==False:
96
+          loTmpDict[loNode.parent_id] = getNode(loNode.parent_id)
97
+        loTmpDict[loNode.parent_id].appendStaticChild(loNode)
98
+  
99
+    return loTreeList
108
 
100
 
109
-  return loTreeList
101
+  def getParentNode(self, loNode):
102
+    liOwnerIdList = self._getUserIdListForFiltering()
103
+    return DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_id==loNode.parent_id).one()
110
 
104
 
105
+  def getSiblingNodes(self, poNode, pbReverseOrder=False):
106
+    liOwnerIdList = self._getUserIdListForFiltering()
107
+    
108
+    if pbReverseOrder:
109
+      return DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.parent_id==poNode.parent_id).order_by(pbmd.PBNode.node_order.desc()).all()
110
+    else:
111
+      return DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.parent_id==poNode.parent_id).order_by(pbmd.PBNode.node_order).all()
112
+
113
+  def resetNodeOrderOfSiblingNodes(self, loSiblingNodes):
114
+    liNewWeight = 0
115
+    for loNode in loSiblingNodes:
116
+      liNewWeight = liNewWeight + 1
117
+      loNode.node_order = liNewWeight
118
+    # DBSession.save()
119
+
120
+  def moveNodeUpper(self, loNode):
121
+    # FIXME - manage errors and logging
122
+    
123
+    loSiblingNodes = self.getSiblingNodes(loNode)
124
+    self.resetNodeOrderOfSiblingNodes(loSiblingNodes)
125
+  
126
+    loPreviousItem = None
127
+    for loItem in loSiblingNodes:
128
+      if loItem==loNode:
129
+        if loPreviousItem==None:
130
+          # FIXME
131
+          print "No previous node"
132
+        else:
133
+          liPreviousItemOrder       = loPreviousItem.node_order
134
+          loPreviousItem.node_order = loNode.node_order
135
+          loNode.node_order         = liPreviousItemOrder
136
+          # DBSession.save()
137
+          break
138
+      loPreviousItem = loItem
139
+
140
+  def moveNodeLower(self, loNode):
141
+    # FIXME - manage errors and logging
142
+    
143
+    loSiblingNodes = self.getSiblingNodes(loNode)
144
+    self.resetNodeOrderOfSiblingNodes(loSiblingNodes)
145
+  
146
+    loPreviousItem = None
147
+    for loItem in reversed(loSiblingNodes):
148
+      if loItem==loNode:
149
+        if loPreviousItem==None:
150
+          # FIXME
151
+          print "No previous node"
152
+        else:
153
+          liPreviousItemOrder       = loPreviousItem.node_order
154
+          loPreviousItem.node_order = loNode.node_order
155
+          loNode.node_order         = liPreviousItemOrder
156
+          # DBSession.save()
157
+          break
158
+      loPreviousItem = loItem
159
+
160
+  def getNodeFileContent(self, liNodeId):
161
+    liOwnerIdList = self._getUserIdListForFiltering()
162
+    return DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_id==liNodeId).one().data_file_content
163
+
164
+  def deleteNode(loNode):
165
+    # INFO - D.A. - 2013-11-07 - should be save as getNode should return only accessible nodes
166
+    DBSession.delete(loNode)
167
+    return

+ 5 - 0
pboard/pboard/model/auth.py View File

60
     def __unicode__(self):
60
     def __unicode__(self):
61
         return self.group_name
61
         return self.group_name
62
 
62
 
63
+    @classmethod
64
+    def by_group_name(cls, group_name):
65
+        """Return the user object whose email address is ``email``."""
66
+        return DBSession.query(cls).filter_by(group_name=group_name).first()
67
+
63
 class User(DeclarativeBase):
68
 class User(DeclarativeBase):
64
     """
69
     """
65
     User definition.
70
     User definition.

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

170
   parent_id        = Column(Integer, ForeignKey('pb_nodes.node_id'), nullable=True, default=None)
170
   parent_id        = Column(Integer, ForeignKey('pb_nodes.node_id'), nullable=True, default=None)
171
   node_depth       = Column(Integer, unique=False, nullable=False, default=0)
171
   node_depth       = Column(Integer, unique=False, nullable=False, default=0)
172
   parent_tree_path = Column(Unicode(255), unique=False, nullable=False, default=u'')
172
   parent_tree_path = Column(Unicode(255), unique=False, nullable=False, default=u'')
173
+  owner_id         = Column(Integer, ForeignKey('tg_user.user_id'), nullable=True, default=None)
173
 
174
 
174
   node_order  = Column(Integer, nullable=True, default=1)
175
   node_order  = Column(Integer, nullable=True, default=1)
175
   node_type   = Column(Unicode(16), unique=False, nullable=False, default=u'data')
176
   node_type   = Column(Unicode(16), unique=False, nullable=False, default=u'data')

+ 248 - 248
pboard/pboard/templates/document.mak View File

90
       </div>
90
       </div>
91
     </div>
91
     </div>
92
     <div id='application-main-panel' class="span9">
92
     <div id='application-main-panel' class="span9">
93
-        
94
-        
95
-        
96
       <div class="btn-group">
93
       <div class="btn-group">
97
         <button class="btn">Status</button>
94
         <button class="btn">Status</button>
98
         <a class="btn ${current_node.getStatus().css}" href="#"><i class="${current_node.getStatus().icon}"></i> ${current_node.getStatus().getLabel()}</a>
95
         <a class="btn ${current_node.getStatus().css}" href="#"><i class="${current_node.getStatus().icon}"></i> ${current_node.getStatus().getLabel()}</a>
117
 
114
 
118
         <a href='${tg.url('/api/force_delete_node?node_id=%i'%(current_node.node_id))}' id='current-document-force-delete-button' class="btn" onclick="return confirm('${_('Delete current document?')}');"><i class="icon-g-remove"></i> ${_('Delete')}</a>
115
         <a href='${tg.url('/api/force_delete_node?node_id=%i'%(current_node.node_id))}' id='current-document-force-delete-button' class="btn" onclick="return confirm('${_('Delete current document?')}');"><i class="icon-g-remove"></i> ${_('Delete')}</a>
119
       </div>
116
       </div>
120
-      
117
+          
121
       <h3 id="current-document-title">#${current_node.node_id} - ${current_node.data_label}</h3>
118
       <h3 id="current-document-title">#${current_node.node_id} - ${current_node.data_label}</h3>
122
-        <form style='display: none; margin-top: 1em;' id="current-document-title-edit-form" method='post' action='${tg.url('/api/edit_label')}'>
123
-          <div class="input-prepend input-append">
124
-            <input type='hidden' name='node_id' value='${current_node.node_id}'/>
125
-            ${POD.CancelButton('current-document-title-edit-cancel-button')}
126
-            <input type='text' name='data_label' value='${current_node.data_label}' class="span2" />
127
-            ${POD.SaveButton('current-document-title-save-cancel-button')}
119
+      <form style='display: none; margin-top: 1em;' id="current-document-title-edit-form" method='post' action='${tg.url('/api/edit_label')}'>
120
+        <div class="input-prepend input-append">
121
+          <input type='hidden' name='node_id' value='${current_node.node_id}'/>
122
+          ${POD.CancelButton('current-document-title-edit-cancel-button')}
123
+          <input type='text' name='data_label' value='${current_node.data_label}' class="span2" />
124
+          ${POD.SaveButton('current-document-title-save-cancel-button')}
125
+        </div>
126
+      </form>
127
+
128
+      <div id='application-document-panel' class="span5">
129
+        <p>
130
+          <div id='current-document-content' class="">
131
+            ${current_node.getContentWithTags()|n}
128
           </div>
132
           </div>
129
-        </form>
133
+          <form style='display: none;' id="current-document-content-edit-form" method='post' action='${tg.url('/api/edit_content')}'>
134
+            <input type='hidden' name='node_id' value='${current_node.node_id}'/>
135
+            <textarea id="current_node_textarea" name='data_content' spellcheck="false" wrap="off" autofocus placeholder="Enter something ...">
136
+              ${current_node.data_content|n}
137
+            </textarea>
138
+            ${POD.CancelButton('current-document-content-edit-cancel-button', True)}
139
+            ${POD.SaveButton('current-document-content-edit-save-button', True)}
140
+          </form>
141
+        </p>
130
       </div>
142
       </div>
131
-      <div id='application-document-panel' class="span5">
132
-      <p>
133
-        <div id='current-document-content' class="">
134
-          ${current_node.getContentWithTags()|n}
135
-        </div>
136
-        <form style='display: none;' id="current-document-content-edit-form" method='post' action='${tg.url('/api/edit_content')}'>
137
-          <input type='hidden' name='node_id' value='${current_node.node_id}'/>
138
-          <textarea id="current_node_textarea" name='data_content' spellcheck="false" wrap="off" autofocus placeholder="Enter something ...">
139
-            ${current_node.data_content|n}
140
-          </textarea>
141
-          ${POD.CancelButton('current-document-content-edit-cancel-button', True)}
142
-          ${POD.SaveButton('current-document-content-edit-save-button', True)}
143
-        </form>
144
-      </p>
145
-    </div>
146
-    <div id='application-metadata-panel' class="span4">
147
-      <div class="tabbable">
148
-        <ul class="nav nav-tabs">
149
-            ## DEBUG - D.A. - 2013-11-07 -  <li class="active"><a href="#tags" data-toggle="tab" title="${_('Tags')}"><i class='icon-g-tags'></i></a></li>
150
-            <li class="active"><a href="#events" data-toggle="tab" title="History"><i class="icon-g-history"></i>${POD.ItemNb(current_node.getEvents())}</a></li>
151
-            <li><a href="#contacts" data-toggle="tab" title="Contacts"><i class="icon-g-user""></i>${POD.ItemNb(current_node.getContacts())}</a></li>
152
-            <li><a href="#comments" data-toggle="tab" title="Comments"><i class="icon-g-conversation"></i>${POD.ItemNb(current_node.getComments())}</a></li>
153
-            <li><a href="#files" data-toggle="tab" title="Files"><i class="icon-g-attach"></i>${POD.ItemNb(current_node.getFiles())}</a></li>
154
-        </ul>
155
-        <div class="tab-content">
156
-            ################################
157
-            ##
158
-            ## PANEL SHOWING LIST OF TAGS
159
-            ##
160
-            ################################
161
-            <!-- DEBUG - D.A. - 2013-11-07 - Not using tags for th moment
162
-            <div class="tab-pane" id="tags">
163
-              <div class="well">
164
-                <p>
165
-                  <i>
166
-                    ${_('Tags are automatically extracted from document content:')}
167
-                    <ul>
168
-                      <li>${_('<code>@visible_keyword</code> is a visible keyword generating a tag.')|n}</li>
169
-                      <li>
170
-                        ${_('<code>@invisible_keyword</code> is an <u>invisible</u> keyword generating a tag.')|n}</li>
171
-                    </ul>
172
-                  </i>
173
-                </p>
174
-                % for tag in current_node.getTagList():
175
-                  ${POD.Badge(tag)}
176
-                % endfor
143
+      ## FIXME - D.A - 2013-11-07 - The following div should be span4 instead of span3 but some bug make it impossible
144
+      <div id='application-metadata-panel' class="span3">
145
+        <div class="tabbable">
146
+          <ul class="nav nav-tabs">
147
+              ## DEBUG - D.A. - 2013-11-07 -  <li class="active"><a href="#tags" data-toggle="tab" title="${_('Tags')}"><i class='icon-g-tags'></i></a></li>
148
+              <li class="active"><a href="#events" data-toggle="tab" title="History"><i class="icon-g-history"></i>${POD.ItemNb(current_node.getEvents())}</a></li>
149
+              <li><a href="#contacts" data-toggle="tab" title="Contacts"><i class="icon-g-user""></i>${POD.ItemNb(current_node.getContacts())}</a></li>
150
+              <li><a href="#comments" data-toggle="tab" title="Comments"><i class="icon-g-conversation"></i>${POD.ItemNb(current_node.getComments())}</a></li>
151
+              <li><a href="#files" data-toggle="tab" title="Files"><i class="icon-g-attach"></i>${POD.ItemNb(current_node.getFiles())}</a></li>
152
+          </ul>
153
+          <div class="tab-content">
154
+              ################################
155
+              ##
156
+              ## PANEL SHOWING LIST OF TAGS
157
+              ##
158
+              ################################
159
+              <!-- DEBUG - D.A. - 2013-11-07 - Not using tags for th moment
160
+              <div class="tab-pane" id="tags">
161
+                <div class="well">
162
+                  <p>
163
+                    <i>
164
+                      ${_('Tags are automatically extracted from document content:')}
165
+                      <ul>
166
+                        <li>${_('<code>@visible_keyword</code> is a visible keyword generating a tag.')|n}</li>
167
+                        <li>
168
+                          ${_('<code>@invisible_keyword</code> is an <u>invisible</u> keyword generating a tag.')|n}</li>
169
+                      </ul>
170
+                    </i>
171
+                  </p>
172
+                  % for tag in current_node.getTagList():
173
+                    ${POD.Badge(tag)}
174
+                  % endfor
175
+                </div>
177
               </div>
176
               </div>
178
-            </div>
179
-            -->
180
-            ################################
181
-            ##
182
-            ## PANEL SHOWING LIST OF EVENTS
183
-            ##
184
-            ################################
185
-            <div class="tab-pane active" id="events">
186
-              ${POD.AddButton('current-document-add-event-button', True, _(' Add event'))}
187
-              <form style='display: none;' id='current-document-add-event-form' action='${tg.url('/api/create_event')}' method='post' class="well">
188
-                <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
189
-                <fieldset>
190
-                  <legend>Add an event</legend>
191
-                  <label>
192
-                    <input type="text" name='data_label' placeholder="Event"/>
193
-                  </label>
194
-                  <label>
195
-                    <div class="datetime-picker-input-div input-append date">
196
-                      <input name='data_datetime' data-format="dd/MM/yyyy hh:mm" type="text" placeholder="date and time"/>
197
-                      <span class="add-on"><i data-time-icon="icon-g-clock" data-date-icon="icon-g-calendar"></i></span>
198
-                    </div>
199
-                  </label>
200
-                  <label>
201
-                    <div>
202
-                      <textarea id="add_event_data_content_textarea" name='data_content' spellcheck="false" wrap="off" autofocus placeholder="${_('detail...')}"></textarea>
203
-                    </div>
204
-                  </label>
205
-                  <label class="checkbox">
206
-                    <input disabled name='add_reminder' type="checkbox"> add a reminder
207
-                  </label>
208
-                  <label>
209
-                    <div class="datetime-picker-input-div input-append date">
210
-                      <input disabled name='data_reminder_datetime' data-format="dd/MM/yyyy hh:mm" type="text" placeholder="date and time"/>
211
-                      <span class="add-on"><i data-time-icon="icon-g-clock" data-date-icon="icon-g-calendar"></i></span>
212
-                    </div>
213
-                  </label>
177
+              -->
178
+              ################################
179
+              ##
180
+              ## PANEL SHOWING LIST OF EVENTS
181
+              ##
182
+              ################################
183
+              <div class="tab-pane active" id="events">
184
+                ${POD.AddButton('current-document-add-event-button', True, _(' Add event'))}
185
+                <form style='display: none;' id='current-document-add-event-form' action='${tg.url('/api/create_event')}' method='post' class="well">
186
+                  <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
187
+                  <fieldset>
188
+                    <legend>Add an event</legend>
189
+                    <label>
190
+                      <input type="text" name='data_label' placeholder="Event"/>
191
+                    </label>
192
+                    <label>
193
+                      <div class="datetime-picker-input-div input-append date">
194
+                        <input name='data_datetime' data-format="dd/MM/yyyy hh:mm" type="text" placeholder="date and time"/>
195
+                        <span class="add-on"><i data-time-icon="icon-g-clock" data-date-icon="icon-g-calendar"></i></span>
196
+                      </div>
197
+                    </label>
198
+                    <label>
199
+                      <div>
200
+                        <textarea id="add_event_data_content_textarea" name='data_content' spellcheck="false" wrap="off" autofocus placeholder="${_('detail...')}"></textarea>
201
+                      </div>
202
+                    </label>
203
+                    <label class="checkbox">
204
+                      <input disabled name='add_reminder' type="checkbox"> add a reminder
205
+                    </label>
206
+                    <label>
207
+                      <div class="datetime-picker-input-div input-append date">
208
+                        <input disabled name='data_reminder_datetime' data-format="dd/MM/yyyy hh:mm" type="text" placeholder="date and time"/>
209
+                        <span class="add-on"><i data-time-icon="icon-g-clock" data-date-icon="icon-g-calendar"></i></span>
210
+                      </div>
211
+                    </label>
214
 
212
 
215
 
213
 
216
-                  ${POD.CancelButton('current-document-add-event-cancel-button', True)}
217
-                  ${POD.SaveButton('current-document-add-event-save-button', True)}
218
-                </fieldset>
219
-              </form>
214
+                    ${POD.CancelButton('current-document-add-event-cancel-button', True)}
215
+                    ${POD.SaveButton('current-document-add-event-save-button', True)}
216
+                  </fieldset>
217
+                </form>
220
 
218
 
221
-            % if len(current_node.getEvents())<=0:
222
-              <p><i>${_('No history for the moment.')}</i></p>
223
-            % else:
224
-              <table class="table table-striped table-hover table-condensed">
225
-                <thead>
226
-                  <tr>
227
-                    <th>Date</th>
228
-                    <th>Time</th>
229
-                    <th>
230
-                      Event
231
-                    </th>
232
-                    <th>
233
-                      <a href="" title="Add an event"><i class="icon-g-plus"></i></a>
234
-                    </th>
235
-                  </tr>
236
-                </thead>
237
-                % for event in current_node.getEvents():
238
-                  <tr class="item-with-data-popoverable" data-content="${event.data_content}" rel="popover" data-placement="left" data-trigger="hover">
239
-                    <td>${event.getFormattedDate(event.data_datetime)}</td>
240
-                    <td>${event.getFormattedTime(event.data_datetime)}</td>
241
-                    <td>${event.data_label}</td>
242
-                  </tr>
243
-                % endfor
244
-              </table>
245
-            % endif
246
-            </div>
247
-            ##############################
248
-            ## 
249
-            ## PANEL SHOWING LIST OF CONTACTS
250
-            ##
251
-            ##############################
252
-            <div class="tab-pane" id="contacts">
253
-            
254
-              <!-- ADD CONTACT FORM -->
255
-              ${POD.AddButton('current-document-add-contact-button', True, _(' Add contact'))}
256
-              <form style='display: none;' id='current-document-add-contact-form' action='${tg.url('/api/create_contact')}' method='post' class="well">
257
-                <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
258
-                <fieldset>
259
-                  <legend>${_('Add a contact')}</legend>
260
-                  <label>
261
-                    <input type="text" name='data_label' placeholder="Title"/>
262
-                  </label>
263
-                  <label>
219
+              % if len(current_node.getEvents())<=0:
220
+                <p><i>${_('No history for the moment.')}</i></p>
221
+              % else:
222
+                <table class="table table-striped table-hover table-condensed">
223
+                  <thead>
224
+                    <tr>
225
+                      <th>Date</th>
226
+                      <th>Time</th>
227
+                      <th>
228
+                        Event
229
+                      </th>
230
+                      <th>
231
+                        <a href="" title="Add an event"><i class="icon-g-plus"></i></a>
232
+                      </th>
233
+                    </tr>
234
+                  </thead>
235
+                  % for event in current_node.getEvents():
236
+                    <tr class="item-with-data-popoverable" data-content="${event.data_content}" rel="popover" data-placement="left" data-trigger="hover">
237
+                      <td>${event.getFormattedDate(event.data_datetime)}</td>
238
+                      <td>${event.getFormattedTime(event.data_datetime)}</td>
239
+                      <td>${event.data_label}</td>
240
+                    </tr>
241
+                  % endfor
242
+                </table>
243
+              % endif
244
+              </div>
245
+              ##############################
246
+              ## 
247
+              ## PANEL SHOWING LIST OF CONTACTS
248
+              ##
249
+              ##############################
250
+              <div class="tab-pane" id="contacts">
251
+              
252
+                <!-- ADD CONTACT FORM -->
253
+                ${POD.AddButton('current-document-add-contact-button', True, _(' Add contact'))}
254
+                <form style='display: none;' id='current-document-add-contact-form' action='${tg.url('/api/create_contact')}' method='post' class="well">
255
+                  <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
256
+                  <fieldset>
257
+                    <legend>${_('Add a contact')}</legend>
258
+                    <label>
259
+                      <input type="text" name='data_label' placeholder="Title"/>
260
+                    </label>
261
+                    <label>
262
+                      <div>
263
+                        <textarea id="add_contact_data_content_textarea" name='data_content' spellcheck="false" wrap="off" autofocus placeholder="${_('detail...')}"></textarea>
264
+                      </div>
265
+                    </label>
266
+                    ${POD.CancelButton('current-document-add-contact-cancel-button', True)}
267
+                    ${POD.SaveButton('current-document-add-contact-save-button', True)}
268
+                  </fieldset>
269
+                </form>
270
+
271
+                <!-- LIST OF CONTACT NODES -->
272
+                % for contact in current_node.getContacts():
273
+                  <div class="well">
274
+                    <legend class="text-info">${contact.data_label}</legend>
264
                     <div>
275
                     <div>
265
-                      <textarea id="add_contact_data_content_textarea" name='data_content' spellcheck="false" wrap="off" autofocus placeholder="${_('detail...')}"></textarea>
276
+                      <a style='float: right;' href="" title='${_('Search on google maps')}'><i class='icon-g-google-maps'></i></a>
277
+                      ${contact.data_content|n}
266
                     </div>
278
                     </div>
267
-                  </label>
268
-                  ${POD.CancelButton('current-document-add-contact-cancel-button', True)}
269
-                  ${POD.SaveButton('current-document-add-contact-save-button', True)}
270
-                </fieldset>
271
-              </form>
272
-
273
-              <!-- LIST OF CONTACT NODES -->
274
-              % for contact in current_node.getContacts():
275
-                <div class="well">
276
-                  <legend class="text-info">${contact.data_label}</legend>
277
-                  <div>
278
-                    <a style='float: right;' href="" title='${_('Search on google maps')}'><i class='icon-g-google-maps'></i></a>
279
-                    ${contact.data_content|n}
280
                   </div>
279
                   </div>
281
-                </div>
282
-              % endfor
280
+                % endfor
283
 
281
 
284
 
282
 
285
-            </div>
286
-            ################################
287
-            ##
288
-            ## PANEL SHOWING LIST OF COMMENTS
289
-            ##
290
-            ################################
291
-            <div class="tab-pane" id="comments">
292
-              <!-- ADD COMMENT FORM -->
293
-              ${POD.AddButton('current-document-add-comment-button', True, _(' Add comment'))}
294
-              <form style='display: none;' id='current-document-add-comment-form' action='${tg.url('/api/create_comment')}' method='post' class="well">
295
-                <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
296
-                <fieldset>
297
-                  <legend>${_('Add a comment')}</legend>
298
-                  <label>
299
-                    <input type="text" name='data_label' placeholder="Title"/>
300
-                  </label>
301
-                  <label>
302
-                    <div>
303
-                      <textarea id="add_comment_data_content_textarea" name='data_content' spellcheck="false" wrap="off" autofocus placeholder="${_('comment...')}"></textarea>
304
-                    </div>
305
-                  </label>
306
-                  ${POD.CancelButton('current-document-add-comment-cancel-button', True)}
307
-                  ${POD.SaveButton('current-document-add-comment-save-button', True)}
308
-                </fieldset>
309
-              </form>
283
+              </div>
284
+              ################################
285
+              ##
286
+              ## PANEL SHOWING LIST OF COMMENTS
287
+              ##
288
+              ################################
289
+              <div class="tab-pane" id="comments">
290
+                <!-- ADD COMMENT FORM -->
291
+                ${POD.AddButton('current-document-add-comment-button', True, _(' Add comment'))}
292
+                <form style='display: none;' id='current-document-add-comment-form' action='${tg.url('/api/create_comment')}' method='post' class="well">
293
+                  <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
294
+                  <fieldset>
295
+                    <legend>${_('Add a comment')}</legend>
296
+                    <label>
297
+                      <input type="text" name='data_label' placeholder="Title"/>
298
+                    </label>
299
+                    <label>
300
+                      <div>
301
+                        <textarea id="add_comment_data_content_textarea" name='data_content' spellcheck="false" wrap="off" autofocus placeholder="${_('comment...')}"></textarea>
302
+                      </div>
303
+                    </label>
304
+                    ${POD.CancelButton('current-document-add-comment-cancel-button', True)}
305
+                    ${POD.SaveButton('current-document-add-comment-save-button', True)}
306
+                  </fieldset>
307
+                </form>
310
 
308
 
311
-              <!-- LIST OF COMMENTS -->
312
-            % if len(current_node.getComments())<=0:
313
-              <p><i>${_('No comments.')}</i></p>
314
-            % else:
315
-              <table class="table table-striped table-hover table-condensed">
316
-                % for comment in current_node.getComments():
317
-                  <tr title="Last updated: ${comment.updated_at}">
318
-                    <td>
319
-                      <i>The ${comment.getFormattedDate(comment.updated_at)} at ${comment.getFormattedTime(comment.updated_at)}, comment.author wrote: </i><br/>
320
-                      <b>${comment.data_label}</b><br/>
321
-                      <p>
322
-                        ${comment.data_content|n}
323
-                      </p>
324
-                    </td>
325
-                  </tr>
326
-                % endfor
327
-              </table>
328
-            % endif
329
-            </div>
330
-            ################################
331
-            ##
332
-            ## PANEL SHOWING LIST OF FILES
333
-            ##
334
-            ################################
335
-            <div class="tab-pane" id="files">
336
-              <!-- ADD FILE FORM -->
337
-              ${POD.AddButton('current-document-add-file-button', True, _(' Add file'))}
338
-              <form style='display: none;' id='current-document-add-file-form' enctype="multipart/form-data" action='${tg.url('/api/create_file')}' method='post' class="well">
339
-                <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
340
-                <fieldset>
341
-                  <legend>${_('Add a file')}</legend>
342
-                  <label>
343
-                    <input type="text" name='data_label' placeholder="Title"/>
344
-                  </label>
345
-                  <label>
346
-                    <input type="file" name='data_file' placeholder="choose a file..."/>
347
-                  </label>
348
-                  <label>
349
-                    <div>
350
-                      <textarea id="add_file_data_content_textarea" name='data_content' spellcheck="false" wrap="off" autofocus placeholder="${_('comment...')}"></textarea>
351
-                    </div>
352
-                  </label>
353
-                  ${POD.CancelButton('current-document-add-file-cancel-button', True)}
354
-                  ${POD.SaveButton('current-document-add-file-save-button', True)}
355
-                </fieldset>
356
-              </form>
309
+                <!-- LIST OF COMMENTS -->
310
+              % if len(current_node.getComments())<=0:
311
+                <p><i>${_('No comments.')}</i></p>
312
+              % else:
313
+                <table class="table table-striped table-hover table-condensed">
314
+                  % for comment in current_node.getComments():
315
+                    <tr title="Last updated: ${comment.updated_at}">
316
+                      <td>
317
+                        <i>The ${comment.getFormattedDate(comment.updated_at)} at ${comment.getFormattedTime(comment.updated_at)}, comment.author wrote: </i><br/>
318
+                        <b>${comment.data_label}</b><br/>
319
+                        <p>
320
+                          ${comment.data_content|n}
321
+                        </p>
322
+                      </td>
323
+                    </tr>
324
+                  % endfor
325
+                </table>
326
+              % endif
327
+              </div>
328
+              ################################
329
+              ##
330
+              ## PANEL SHOWING LIST OF FILES
331
+              ##
332
+              ################################
333
+              <div class="tab-pane" id="files">
334
+                <!-- ADD FILE FORM -->
335
+                ${POD.AddButton('current-document-add-file-button', True, _(' Add file'))}
336
+                <form style='display: none;' id='current-document-add-file-form' enctype="multipart/form-data" action='${tg.url('/api/create_file')}' method='post' class="well">
337
+                  <input type="hidden" name='parent_id' value='${current_node.node_id}'/>
338
+                  <fieldset>
339
+                    <legend>${_('Add a file')}</legend>
340
+                    <label>
341
+                      <input type="text" name='data_label' placeholder="Title"/>
342
+                    </label>
343
+                    <label>
344
+                      <input type="file" name='data_file' placeholder="choose a file..."/>
345
+                    </label>
346
+                    <label>
347
+                      <div>
348
+                        <textarea id="add_file_data_content_textarea" name='data_content' spellcheck="false" wrap="off" autofocus placeholder="${_('comment...')}"></textarea>
349
+                      </div>
350
+                    </label>
351
+                    ${POD.CancelButton('current-document-add-file-cancel-button', True)}
352
+                    ${POD.SaveButton('current-document-add-file-save-button', True)}
353
+                  </fieldset>
354
+                </form>
357
 
355
 
358
-              <!-- LIST OF FILES -->
359
-            % if len(current_node.getFiles())<=0:
360
-              <p><i>${_('No files.')}</i></p>
361
-            % else:
362
-              <table class="table table-striped table-hover table-condensed">
363
-                % for current_file in current_node.getFiles():
364
-                  <tr title="Last updated: ${current_file.updated_at}">
365
-                    <td>
366
-                      <img src="${tg.url('/api/get_file_content_thumbnail/%s'%(current_file.node_id))}" class="img-polaroid">
367
-                    </td>
368
-                    <td>
369
-                      <b>${current_file.data_label}</b><br/>
370
-                      <i>commented by comment.author the ${current_file.getFormattedDate(current_file.updated_at)} at ${current_file.getFormattedTime(current_file.updated_at)}</i></br>
371
-                      <p>
372
-                        ${current_file.data_content|n}
373
-                      </p>
374
-                    </td>
375
-                  </tr>
376
-                % endfor
377
-              </table>
378
-            % endif
356
+                <!-- LIST OF FILES -->
357
+              % if len(current_node.getFiles())<=0:
358
+                <p><i>${_('No files.')}</i></p>
359
+              % else:
360
+                <table class="table table-striped table-hover table-condensed">
361
+                  % for current_file in current_node.getFiles():
362
+                    <tr title="Last updated: ${current_file.updated_at}">
363
+                      <td>
364
+                        <img src="${tg.url('/api/get_file_content_thumbnail/%s'%(current_file.node_id))}" class="img-polaroid">
365
+                      </td>
366
+                      <td>
367
+                        <b>${current_file.data_label}</b><br/>
368
+                        <i>commented by comment.author the ${current_file.getFormattedDate(current_file.updated_at)} at ${current_file.getFormattedTime(current_file.updated_at)}</i></br>
369
+                        <p>
370
+                          ${current_file.data_content|n}
371
+                        </p>
372
+                      </td>
373
+                    </tr>
374
+                  % endfor
375
+                </table>
376
+              % endif
377
+              </div>
379
             </div>
378
             </div>
379
+          </div>
380
         </div>
380
         </div>
381
       </div>
381
       </div>
382
     </div>
382
     </div>

+ 2 - 2
pboard/pboard/templates/index.mak View File

32
       </div>
32
       </div>
33
     </div>
33
     </div>
34
     <div class="span3">
34
     <div class="span3">
35
-      <form class="well">
35
+      <form class="well" action="${tg.url('/public_api/create_account')}">
36
         <fieldset>
36
         <fieldset>
37
           <legend>Sign up</legend>
37
           <legend>Sign up</legend>
38
           <input type="text" id="email" placeholder="Email"><br/>
38
           <input type="text" id="email" placeholder="Email"><br/>
39
           <input type="text" id="password" placeholder="Password"><br/>
39
           <input type="text" id="password" placeholder="Password"><br/>
40
-          <input type="text" id="retype_password" placeholder="Retype your password"><br/>
40
+          <input type="text" id="retyped_password" placeholder="Retype your password"><br/>
41
           <input type="submit" id="submit" value="Sign up" /><br/>
41
           <input type="submit" id="submit" value="Sign up" /><br/>
42
         </fieldset>
42
         </fieldset>
43
       </form>
43
       </form>