Browse Source

implement smaller left panel tree

Damien Accorsi 10 years ago
parent
commit
0a3b67183e

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

@@ -121,7 +121,8 @@ class RootController(BaseController):
121 121
         loCurrentUser   = pld.PODStaticController.getCurrentUser()
122 122
         loApiController = pld.PODUserFilteredApiController(loCurrentUser.user_id)
123 123
 
124
-        loRootNodeList = loApiController.buildTreeListForMenu(pbmd.PBNodeStatus.getVisibleIdsList())
124
+        llAccessibleNodes = loApiController.getListOfAllowedNodes()
125
+
125 126
         liNodeId         = int(node_id)
126 127
         liVersionId      = int(version)
127 128
 
@@ -141,9 +142,12 @@ class RootController(BaseController):
141 142
 
142 143
         # FIXME - D.A - 2013-11-07 - Currently, the code build a new item if no item found for current user
143 144
         # the correct behavior should be to redirect to setup page
145
+        loMenuItems = loApiController.buildTreeListForMenu(loCurrentNode, pbmd.PBNodeStatus.getVisibleIdsList(), llAccessibleNodes)
146
+        nodes_as_tree_for_select_field = loApiController.DIRTY_OLDbuildTreeListForMenu(pbmd.PBNodeStatus.getVisibleIdsList())
144 147
 
145 148
         return dict(
146
-            root_node_list=loRootNodeList,
149
+            menu_node_list=loMenuItems,
150
+            root_node_list_for_select_field=nodes_as_tree_for_select_field,
147 151
             current_node=loCurrentNode,
148 152
             node_status_list = loNodeStatusList,
149 153
             keywords = highlight,

+ 72 - 1
pboard/pboard/lib/dbapi.py View File

@@ -19,6 +19,7 @@ import pboard.model as pbm
19 19
 import tg
20 20
 
21 21
 FIXME_ERROR_CODE=-1
22
+
22 23
 class PODStaticController(object):
23 24
 
24 25
   @classmethod
@@ -144,6 +145,30 @@ class PODUserFilteredApiController(object):
144 145
     return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).order_by(pbmd.PBNode.updated_at.desc()).limit(piMaxNodeNb).all()
145 146
 
146 147
 
148
+  def getListOfAllowedNodes(self) -> pbmd.PBNode:
149
+    lsSqlQuery = """
150
+        SELECT
151
+            pgn.node_id
152
+        FROM
153
+            pod_group_node AS pgn
154
+            join pod_user_group AS pug ON pug.group_id = pgn.group_id
155
+        WHERE
156
+            pgn.rights > 0
157
+            AND pug.user_id = :owner_id
158
+        UNION
159
+            SELECT
160
+                node_id
161
+            FROM
162
+                pod_nodes
163
+            WHERE
164
+            owner_id=:owner_id;
165
+    """
166
+
167
+    loNodeListResult = DBSession.query(pbmd.PBNode).from_statement(lsSqlQuery).params(owner_id=self._iCurrentUserId)
168
+
169
+    return loNodeListResult.all()
170
+
171
+
147 172
   def searchNodesByText(self, plKeywordList: [str], piMaxNodeNb=100):
148 173
     """
149 174
     Returns a list of nodes order by type, nodes which contain at least one of the keywords
@@ -171,7 +196,53 @@ class PODUserFilteredApiController(object):
171 196
     return DBSession.query(pbmd.PBNode).options(joinedload_all("_lAllChildren")).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_status==psNodeStatus).order_by(pbmd.PBNode.updated_at).limit(piMaxNodeNb).all()
172 197
 
173 198
 
174
-  def buildTreeListForMenu(self, plViewableStatusId):
199
+  def buildTreeListForMenu(self, poCurrentNode: pbmd.PBNode, plViewableStatusId: [], plAllowedNodes: [pbmd.PBNode]) -> [pbmd.NodeTreeItem]:
200
+    # The algorithm is:
201
+    # 1. build an intermediate tree containing only current node and parent path
202
+    #    + complete it with sibling at each level (except root)
203
+    # 2. add sibling nodes at root level
204
+    # 3. complete it with shared documents (which are not at root but shared with current user)
205
+
206
+    node_tree = []
207
+
208
+    previous_tree_item = None
209
+    tmp_children_nodes = []
210
+
211
+    if poCurrentNode is not None:
212
+        breadcrumb_nodes = poCurrentNode.getBreadCrumbNodes()
213
+        breadcrumb_nodes.append(poCurrentNode) # by default the current node is not included
214
+
215
+        for breadcrumb_node in reversed(breadcrumb_nodes):
216
+            if previous_tree_item is None:
217
+                # First iteration. We add all current_node children
218
+                for child_node in breadcrumb_node.getChildren():
219
+                    child_item = pbmd.NodeTreeItem(child_node, [])
220
+                    tmp_children_nodes.append(child_item)
221
+                previous_tree_item = pbmd.NodeTreeItem(breadcrumb_node, tmp_children_nodes)
222
+            else:
223
+                tmp_children_nodes = []
224
+                for child_node in breadcrumb_node.getChildren():
225
+                    if child_node == previous_tree_item.node:
226
+                        tmp_children_nodes.append(previous_tree_item)
227
+                    else:
228
+                        sibling_node = pbmd.NodeTreeItem(child_node, [])
229
+                        tmp_children_nodes.append(sibling_node)
230
+
231
+                previous_tree_item = pbmd.NodeTreeItem(breadcrumb_node, tmp_children_nodes)
232
+
233
+    for node in plAllowedNodes:
234
+        if node.parent_id==0 or node.parent_id is None:
235
+            if previous_tree_item is not None and node == previous_tree_item.node:
236
+                node_tree.append(previous_tree_item)
237
+            else:
238
+                node_tree.append(pbmd.NodeTreeItem(node, []))
239
+
240
+    return node_tree
241
+
242
+
243
+
244
+  def DIRTY_OLDbuildTreeListForMenu(self, plViewableStatusId: []) -> [pbmd.PBNode]:
245
+
175 246
     liOwnerIdList = self._getUserIdListForFiltering()
176 247
     
177 248
     loNodeList = pbm.DBSession.query(pbmd.PBNode).filter(pbmd.PBNode.owner_id.in_(liOwnerIdList)).filter(pbmd.PBNode.node_type==pbmd.PBNodeType.Data).filter(pbmd.PBNode.node_status.in_(plViewableStatusId)).order_by(pbmd.PBNode.parent_tree_path).order_by(pbmd.PBNode.node_order).order_by(pbmd.PBNode.node_id).all()

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

@@ -272,7 +272,7 @@ class PBNode(DeclarativeBase):
272 272
     else:
273 273
       return PBNode.getIconClassForNodeType(self.node_type)
274 274
 
275
-  def getBreadCrumbNodes(self):
275
+  def getBreadCrumbNodes(self) -> list('PBNode'):
276 276
     loNodes = []
277 277
     if self._oParent!=None:
278 278
       loNodes = self._oParent.getBreadCrumbNodes()
@@ -403,6 +403,18 @@ class PBNode(DeclarativeBase):
403 403
       return DBSession.execute("select node_id, version_id, created_at from pod_nodes_history where node_id = :node_id order by created_at desc", {"node_id":self.node_id}).fetchall()
404 404
 
405 405
 
406
+class NodeTreeItem(object):
407
+    """
408
+        This class implements a model that allow to simply represents the left-panel menu items
409
+         This model is used by dbapi but is not directly related to sqlalchemy and database
410
+    """
411
+    def __init__(self, node: PBNode, children: list('NodeTreeItem')):
412
+        self.node = node
413
+        self.children = children
414
+
415
+
416
+
417
+
406 418
 #####
407 419
 #
408 420
 # HACK - 2014-05-21 - D.A

+ 32 - 41
pboard/pboard/templates/document.mak View File

@@ -11,45 +11,36 @@
11 11
   % endif
12 12
 </%def>
13 13
 
14
-<%def name="node_treeview(node_list, indentation=-1)">
15
-  % if indentation==-1:
16
-    <div id='pod-menu-item-0' class="pod-toolbar-parent" style="padding-left: 0.5em; position: relative;">
17
-      <a class="toggle-child-menu-items"><i class='fa fa-folder-open'></i></a>
18
-      <a href="${tg.url('/document')}" title="${_('Root')}">
19
-        ${_('Root')}
20
-      </a>
21
-    </div>
22
-    <div id="pod-menu-item-0-children">${node_treeview(node_list, 0)}</div>
23
-    
24
-  % else:
25
-    % if len(node_list)<=0 and indentation==0:
26
-      <p class="pod-grey">${_('You have no document yet.')}</p>
27
-    % endif
28
-    
29
-    % if len(node_list)>0:
30
-      % for node in node_list:
31
-        <div id='pod-menu-item-${node.node_id}' class="pod-toolbar-parent ${'pod-status-active' if current_node!=None and node.node_id==current_node.node_id else ''}" style="padding-left: ${(indentation+2)*0.5}em; position: relative;">
32
-          <a class="toggle-child-menu-items"><i class='${node.getIconClass()}'></i></a>
33
-          <a href="${tg.url('/document/%s'%(node.node_id))}" title="${node.data_label}">
34
-            % if node.getStatus().status_family=='closed' or node.getStatus().status_family=='invisible':
35
-              <strike>
36
-            % endif
37
-                ${node.getTruncatedLabel(32-0.8*(indentation+1))}
38
-            % if node.getStatus().status_family=='closed' or node.getStatus().status_family=='invisible':
39
-              </strike>
40
-            % endif
41
-          </a>
42
-          <div class="pod-toolbar">
43
-            <a href="${tg.url('/api/move_node_upper?node_id=%i'%(node.node_id))}" title="${_('Move up')}"><i class="fa fa-arrow-up"></i></a>
44
-            <a href="${tg.url('/api/move_node_lower?node_id=%i'%(node.node_id))}" title="${_('Move down')}"><i class="fa fa-arrow-down"></i></a>
45
-          </div>
46
-          <div class="pod-status ${node.getStatus().css}" title='${node.getStatus().label}'>
47
-             <i class='${node.getStatus().icon}'></i>
48
-          </div>
14
+<%def name="node_treeview(node_list, indentation=0)">
15
+  % if len(node_list)<=0 and indentation==0:
16
+    <p class="pod-grey">${_('You have no document yet.')}</p>
17
+  % endif
18
+
19
+  % if len(node_list)>0:
20
+    % for item in node_list:
21
+      <div id='pod-menu-item-${item.node.node_id}' class="pod-toolbar-parent ${'pod-status-active' if current_node!=None and item.node.node_id==current_node.node_id else ''}" style="padding-left: ${(indentation+2)*0.5}em; position: relative;">
22
+        <a class="toggle-child-menu-items"><i class='${item.node.getIconClass()}'></i></a>
23
+        <a href="${tg.url('/document/%s'%(item.node.node_id))}" title="${item.node.data_label}">
24
+          % if item.node.getStatus().status_family=='closed' or item.node.getStatus().status_family=='invisible':
25
+            <strike>
26
+          % endif
27
+              ${item.node.getTruncatedLabel(32-0.8*(indentation+1))}
28
+          % if item.node.getStatus().status_family=='closed' or item.node.getStatus().status_family=='invisible':
29
+            </strike>
30
+          % endif
31
+        </a>
32
+        <div class="pod-toolbar">
33
+          <a href="${tg.url('/api/move_node_upper?node_id=%i'%(item.node.node_id))}" title="${_('Move up')}"><i class="fa fa-arrow-up"></i></a>
34
+          <a href="${tg.url('/api/move_node_lower?node_id=%i'%(item.node.node_id))}" title="${_('Move down')}"><i class="fa fa-arrow-down"></i></a>
49 35
         </div>
50
-        <div id="pod-menu-item-${node.node_id}-children">${node_treeview(node.getStaticChildList(), indentation+1)}</div>
51
-      % endfor
52
-    % endif
36
+        <div class="pod-status ${item.node.getStatus().css}" title='${item.node.getStatus().label}'>
37
+           <i class='${item.node.getStatus().icon}'></i>
38
+        </div>
39
+      </div>
40
+      % if len(item.children)>0:
41
+        <div id="pod-menu-item-${item.node.node_id}-children">${node_treeview(node_list=item.children, indentation=indentation+1)}</div>
42
+      % endif
43
+    % endfor
53 44
   % endif
54 45
 </%def>
55 46
 
@@ -80,7 +71,7 @@
80 71
   <div class="row">
81 72
     <div id='application-left-panel' class="span3">
82 73
       <div>
83
-        ${node_treeview(root_node_list)}
74
+        ${node_treeview(menu_node_list)}
84 75
       </div>
85 76
     </div>
86 77
     <div id='application-main-panel' class="span9">
@@ -100,7 +91,7 @@
100 91
             ##
101 92
             ## The Toolbar is a div with a specific id
102 93
             ##
103
-            ${DOC.Toolbar(current_node, node_status_list, root_node_list, 'current-document-toobar')}
94
+            ${DOC.Toolbar(current_node, node_status_list, root_node_list_for_select_field, 'current-document-toobar')}
104 95
             ${DOC.ShowTitle(current_node, keywords, 'current-document-title')}
105 96
             ${DOC.ShowContent(current_node, keywords)}
106 97
           </div>
@@ -137,7 +128,7 @@
137 128
               <div class="tab-pane" id="contacts">${DOCTABS.ContactTabContent(current_node)}</div>
138 129
               <div class="tab-pane" id="comments">${DOCTABS.CommentTabContent(current_node)}</div>
139 130
               <div class="tab-pane" id="files">${DOCTABS.FileTabContent(current_node)}</div>
140
-			  <div class="tab-pane" id="history">${DOCTABS.HistoryTabContent(current_node)}</div>
131
+              <div class="tab-pane" id="history">${DOCTABS.HistoryTabContent(current_node)}</div>
141 132
               <div class="tab-pane" id="accessmanagement">${DOCTABS.AccessManagementTab(current_node)}</div>
142 133
             </div>
143 134
           </div>