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,7 +16,40 @@ from pboard.lib   import dbapi as pld
16 16
 from pboard.model import data as pmd
17 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 54
 class PODApiController(BaseController):
22 55
     """Sample controller-wide authorization"""
@@ -33,8 +66,10 @@ class PODApiController(BaseController):
33 66
     
34 67
     @expose()
35 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 73
       loNewNode.parent_id     = int(parent_id)
39 74
       loNewNode.node_type     = pmd.PBNodeType.Event
40 75
       loNewNode.data_label    = data_label
@@ -48,8 +83,10 @@ class PODApiController(BaseController):
48 83
 
49 84
     @expose()
50 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 90
       loNewNode.parent_id     = int(parent_id)
54 91
       loNewNode.node_type     = pmd.PBNodeType.Contact
55 92
       loNewNode.data_label    = data_label
@@ -60,8 +97,10 @@ class PODApiController(BaseController):
60 97
 
61 98
     @expose()
62 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 104
       loNewNode.parent_id     = int(parent_id)
66 105
       loNewNode.node_type     = pmd.PBNodeType.Comment
67 106
       loNewNode.data_label    = data_label
@@ -72,8 +111,10 @@ class PODApiController(BaseController):
72 111
 
73 112
     @expose()
74 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 118
       loNewNode.parent_id     = int(parent_id)
78 119
       loNewNode.node_type     = pmd.PBNodeType.File
79 120
       loNewNode.data_label    = data_label
@@ -91,7 +132,10 @@ class PODApiController(BaseController):
91 132
       if node_id==None:
92 133
         return
93 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 139
         tg.response.headers['Content-type'] = str(loFile.data_file_mime_type)
96 140
         return loFile.data_file_content
97 141
 
@@ -100,7 +144,10 @@ class PODApiController(BaseController):
100 144
       if node_id==None:
101 145
         return
102 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 152
         loJpegBytes = csio.StringIO(loFile.data_file_content)
106 153
         loImage     = pil.open(loJpegBytes)
@@ -113,7 +160,11 @@ class PODApiController(BaseController):
113 160
 
114 161
     @expose()
115 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 168
       if new_parent_id!='':
118 169
         loNewNode.parent_id = int(new_parent_id)
119 170
       pm.DBSession.flush()
@@ -121,19 +172,28 @@ class PODApiController(BaseController):
121 172
 
122 173
     @expose()
123 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 180
       redirect(lurl('/document/%s'%(node_id)))
127 181
 
128 182
     @expose()
129 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 189
       redirect(lurl('/document/%s'%(node_id)))
133 190
 
134 191
     @expose()
135 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 197
       loNewNode.data_label   = 'New document'
138 198
       loNewNode.data_content = 'insert content...'
139 199
       if int(parent_id)==0:
@@ -146,25 +206,36 @@ class PODApiController(BaseController):
146 206
 
147 207
     @expose()
148 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 214
       redirect(lurl('/document/%s'%(node_id)))
152 215
 
153 216
     @expose()
154 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 223
       redirect(lurl('/document/%s'%(node_id)))
158 224
 
159 225
     @expose()
160 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 232
       redirect(lurl('/document/%s'%(node_id)))
164 233
 
165 234
     @expose()
166 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 239
       liParentId = loNode.parent_id
169 240
       if loNode.getChildNb()<=0:
170 241
         pm.DBSession.delete(loNode)
@@ -172,7 +243,7 @@ class PODApiController(BaseController):
172 243
 
173 244
     @expose()
174 245
     def reindex_nodes(self, back_to_node_id=0):
175
-      """show the user dashboard"""
246
+      # FIXME - NOT SAFE
176 247
       loRootNodeList   = pm.DBSession.query(pmd.PBNode).order_by(pmd.PBNode.parent_id).all()
177 248
       for loNode in loRootNodeList:
178 249
         if loNode.parent_id==None:

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

@@ -18,6 +18,8 @@ from pboard.lib import dbapi as pld
18 18
 from pboard.controllers import api as pbca
19 19
 from pboard.controllers import debug as pbcd
20 20
 
21
+from pboard import model as pm
22
+
21 23
 import pboard.model.data as pbmd
22 24
 
23 25
 __all__ = ['RootController']
@@ -43,6 +45,8 @@ class RootController(BaseController):
43 45
     debug = pbcd.DebugController()
44 46
     error = ErrorController()
45 47
 
48
+    public_api = pbca.PODPublicApiController()
49
+
46 50
     def _before(self, *args, **kw):
47 51
         tmpl_context.project_name = "pboard"
48 52
 
@@ -90,12 +94,29 @@ class RootController(BaseController):
90 94
     @require(predicates.in_group('user', msg=l_('Please login to access this page')))
91 95
     def document(self, node=0, came_from=lurl('/')):
92 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 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 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,7 +4,6 @@
4 4
 import os
5 5
 from datetime import datetime
6 6
 from hashlib import sha256
7
-__all__ = ['User', 'Group', 'Permission']
8 7
 
9 8
 from sqlalchemy import Table, ForeignKey, Column
10 9
 from sqlalchemy.types import Unicode, Integer, DateTime, Text
@@ -13,98 +12,156 @@ from sqlalchemy.orm import joinedload_all
13 12
 
14 13
 from pboard.model import DeclarativeBase, metadata, DBSession
15 14
 from pboard.model import data as pbmd
15
+from pboard.model import auth as pbma
16 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 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,6 +60,11 @@ class Group(DeclarativeBase):
60 60
     def __unicode__(self):
61 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 68
 class User(DeclarativeBase):
64 69
     """
65 70
     User definition.

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

@@ -170,6 +170,7 @@ class PBNode(DeclarativeBase):
170 170
   parent_id        = Column(Integer, ForeignKey('pb_nodes.node_id'), nullable=True, default=None)
171 171
   node_depth       = Column(Integer, unique=False, nullable=False, default=0)
172 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 175
   node_order  = Column(Integer, nullable=True, default=1)
175 176
   node_type   = Column(Unicode(16), unique=False, nullable=False, default=u'data')

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

@@ -90,9 +90,6 @@ POD :: ${current_node.getTruncatedLabel(40)} [${current_node.getStatus().label}]
90 90
       </div>
91 91
     </div>
92 92
     <div id='application-main-panel' class="span9">
93
-        
94
-        
95
-        
96 93
       <div class="btn-group">
97 94
         <button class="btn">Status</button>
98 95
         <a class="btn ${current_node.getStatus().css}" href="#"><i class="${current_node.getStatus().icon}"></i> ${current_node.getStatus().getLabel()}</a>
@@ -117,266 +114,269 @@ POD :: ${current_node.getTruncatedLabel(40)} [${current_node.getStatus().label}]
117 114
 
118 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 116
       </div>
120
-      
117
+          
121 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 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 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 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 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 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 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 378
             </div>
379
+          </div>
380 380
         </div>
381 381
       </div>
382 382
     </div>

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

@@ -32,12 +32,12 @@
32 32
       </div>
33 33
     </div>
34 34
     <div class="span3">
35
-      <form class="well">
35
+      <form class="well" action="${tg.url('/public_api/create_account')}">
36 36
         <fieldset>
37 37
           <legend>Sign up</legend>
38 38
           <input type="text" id="email" placeholder="Email"><br/>
39 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 41
           <input type="submit" id="submit" value="Sign up" /><br/>
42 42
         </fieldset>
43 43
       </form>