16 |
16 |
17 def treecookiename(treeid): |
17 def treecookiename(treeid): |
18 return str('treestate-%s' % treeid) |
18 return str('treestate-%s' % treeid) |
19 |
19 |
20 |
20 |
|
21 from cubicweb.web.views.baseviews import OneLineView |
|
22 |
21 class TreeView(EntityView): |
23 class TreeView(EntityView): |
22 id = 'treeview' |
24 id = 'treeview' |
23 itemvid = 'treeitemview' |
25 itemvid = 'treeitemview' |
24 css_classes = 'treeview widget' |
26 css_classes = 'treeview widget' |
25 title = _('tree view') |
27 title = _('tree view') |
26 |
28 |
27 def call(self, subvid=None, treeid=None, initial_load=True): |
29 def call(self, subvid=None): |
28 if subvid is None and 'subvid' in self.req.form: |
30 if subvid is None and 'subvid' in self.req.form: |
29 subvid = self.req.form.pop('subvid') # consume it |
31 subvid = self.req.form.pop('subvid') # consume it |
30 if subvid is None: |
32 if subvid is None: |
31 subvid = 'oneline' |
33 subvid = 'oneline' |
32 if treeid is None and 'treeid' in self.req.form: |
34 self.req.add_css('jquery.treeview.css') |
33 treeid = self.req.form.pop('treeid') |
35 self.req.add_js(('cubicweb.ajax.js', 'jquery.treeview.js', 'cubicweb.widgets.js')) |
34 assert treeid is not None |
36 # XXX noautoload is a quick hack to avoid treeview to be rebuilt |
35 if initial_load: |
37 # after a json query and avoid double toggling bugs. |
36 self.req.add_css('jquery.treeview.css') |
38 # Need to find a way to do that cleanly. |
37 self.req.add_js(('cubicweb.ajax.js', 'jquery.treeview.js')) |
39 if 'noautoload' in self.req.form: |
38 self.req.html_headers.add_onload(u""" |
40 self.w(u'<ul class="%s" cubicweb:wdgtype="TreeView">' % self.css_classes) |
39 jQuery("#tree-%s").treeview({toggle: toggleTree, prerendered: true});""" % treeid) |
41 else: |
40 self.w(u'<ul id="tree-%s" class="%s">' % (treeid, self.css_classes)) |
42 self.w(u'<ul class="%s" cubicweb:loadtype="auto" cubicweb:wdgtype="TreeView">' |
|
43 % self.css_classes) |
41 for rowidx in xrange(len(self.rset)): |
44 for rowidx in xrange(len(self.rset)): |
42 self.wview(self.itemvid, self.rset, row=rowidx, col=0, |
45 self.wview(self.itemvid, self.rset, row=rowidx, col=0, |
43 vid=subvid, parentvid=self.id, treeid=treeid) |
46 vid=subvid, parentvid=self.id, treeid=treeid) |
44 self.w(u'</ul>') |
47 self.w(u'</ul>') |
45 |
48 |
52 title = _('file tree view') |
55 title = _('file tree view') |
53 |
56 |
54 def call(self, subvid=None, treeid=None, initial_load=True): |
57 def call(self, subvid=None, treeid=None, initial_load=True): |
55 super(FileTreeView, self).call(treeid=treeid, subvid='filetree-oneline', initial_load=initial_load) |
58 super(FileTreeView, self).call(treeid=treeid, subvid='filetree-oneline', initial_load=initial_load) |
56 |
59 |
|
60 |
|
61 |
57 class FileItemInnerView(EntityView): |
62 class FileItemInnerView(EntityView): |
58 """inner view used by the TreeItemView instead of oneline view |
63 """inner view used by the TreeItemView instead of oneline view |
59 |
64 |
60 This view adds an enclosing <span> with some specific CSS classes |
65 This view adds an enclosing <span> with some specific CSS classes |
61 around the oneline view. This is needed by the jquery treeview plugin. |
66 around the oneline view. This is needed by the jquery treeview plugin. |
63 id = 'filetree-oneline' |
68 id = 'filetree-oneline' |
64 |
69 |
65 def cell_call(self, row, col): |
70 def cell_call(self, row, col): |
66 entity = self.entity(row, col) |
71 entity = self.entity(row, col) |
67 if ITree.is_implemented_by(entity.__class__) and not entity.is_leaf(): |
72 if ITree.is_implemented_by(entity.__class__) and not entity.is_leaf(): |
68 self.w(u'<div class="folder">%s</div>\n' % entity.view('oneline')) |
73 self.w(u'<div class="folder">%s</div>' % entity.view('oneline')) |
69 else: |
74 else: |
70 # XXX define specific CSS classes according to mime types |
75 # XXX define specific CSS classes according to mime types |
71 self.w(u'<div class="file">%s</div>\n' % entity.view('oneline')) |
76 self.w(u'<div class="file">%s</div>' % entity.view('oneline')) |
72 |
77 |
73 |
78 |
74 class DefaultTreeViewItemView(EntityView): |
79 class DefaultTreeViewItemView(EntityView): |
75 """default treeitem view for entities which don't implement ITree""" |
80 """default treeitem view for entities which don't implement ITree""" |
76 id = 'treeitemview' |
81 id = 'treeitemview' |
77 |
82 |
78 def cell_call(self, row, col, vid='oneline', parentvid='treeview', treeid=None): |
83 def cell_call(self, row, col, vid='oneline', parentvid='treeview'): |
79 assert treeid is not None |
|
80 entity = self.entity(row, col) |
84 entity = self.entity(row, col) |
81 itemview = self.view(vid, self.rset, row=row, col=col) |
85 itemview = self.view(vid, self.rset, row=row, col=col) |
82 if row == len(self.rset) - 1: |
86 if row == len(self.rset) - 1: |
83 self.w(u'<li class="last">%s</li>' % itemview) |
87 self.w(u'<li class="last">%s</li>' % itemview) |
84 else: |
88 else: |
90 |
94 |
91 (each item should be expandable if it's not a tree leaf) |
95 (each item should be expandable if it's not a tree leaf) |
92 """ |
96 """ |
93 id = 'treeitemview' |
97 id = 'treeitemview' |
94 __select_ = implements(ITree) |
98 __select_ = implements(ITree) |
95 |
99 |
96 def open_state(self, eeid, treeid): |
100 def cell_call(self, row, col, vid='oneline', parentvid='treeview'): |
97 cookies = self.req.get_cookie() |
|
98 treestate = cookies.get(treecookiename(treeid)) |
|
99 if treestate: |
|
100 return str(eeid) in treestate.value.split(';') |
|
101 return False |
|
102 |
|
103 def cell_call(self, row, col, treeid, vid='oneline', parentvid='treeview'): |
|
104 w = self.w |
|
105 entity = self.entity(row, col) |
101 entity = self.entity(row, col) |
106 liclasses = [] |
102 cssclasses = [] |
107 is_leaf = False |
103 is_leaf = False |
108 is_last = row == len(self.rset) - 1 |
104 if row == len(self.rset) - 1: |
109 is_open = self.open_state(entity.eid, treeid) |
105 is_leaf = True |
110 if not hasattr(entity, 'is_leaf') or entity.is_leaf(): |
106 if not hasattr(entity, 'is_leaf') or entity.is_leaf(): |
111 if is_last: |
107 if is_leaf : cssclasses.append('last') |
112 liclasses.append('last') |
108 self.w(u'<li class="%s">' % u' '.join(cssclasses)) |
113 w(u'<li class="%s">' % u' '.join(liclasses)) |
|
114 else: |
109 else: |
115 rql = entity.children_rql() % {'x': entity.eid} |
110 rql = entity.children_rql() % {'x': entity.eid} |
116 url = html_escape(self.build_url('json', rql=rql, vid=parentvid, |
111 url = html_escape(self.build_url('json', rql=rql, vid=parentvid, |
117 pageid=self.req.pageid, |
112 pageid=self.req.pageid, |
118 treeid=treeid, |
113 subvid=vid, |
119 subvid=vid)) |
114 noautoload=True)) |
120 divclasses = ['hitarea'] |
115 cssclasses.append('expandable') |
121 if is_open: |
116 divclasses = ['hitarea expandable-hitarea'] |
122 liclasses.append('collapsable') |
117 if is_leaf : |
123 divclasses.append('collapsable-hitarea') |
118 cssclasses.append('lastExpandable') |
124 else: |
119 divclasses.append('lastExpandable-hitarea') |
125 liclasses.append('expandable') |
120 self.w(u'<li cubicweb:loadurl="%s" class="%s">' % (url, u' '.join(cssclasses))) |
126 divclasses.append('closed-hitarea expandable-hitarea') |
121 self.w(u'<div class="%s"> </div>' % u' '.join(divclasses)) |
127 if is_last: |
|
128 if is_open: |
|
129 liclasses.append('lastCollapsable') |
|
130 divclasses.append('lastCollapsable-hitarea') |
|
131 else: |
|
132 liclasses.append('lastExpandable') |
|
133 divclasses.append('lastExpandable-hitarea') |
|
134 if is_open: |
|
135 w(u'<li class="%s">' % u' '.join(liclasses)) |
|
136 else: |
|
137 w(u'<li cubicweb:loadurl="%s" class="%s">' % (url, u' '.join(liclasses))) |
|
138 if is_leaf: |
|
139 divtail = '' |
|
140 else: |
|
141 divtail = ''' onclick="async_remote_exec('node_clicked', '%s', '%s')"''' % \ |
|
142 (treeid, entity.eid) |
|
143 w(u'<div class="%s"%s></div>' % (u' '.join(divclasses), divtail)) |
|
144 |
|
145 # add empty <ul> because jquery's treeview plugin checks for |
122 # add empty <ul> because jquery's treeview plugin checks for |
146 # sublists presence |
123 # sublists presence |
147 if not is_open: |
124 self.w(u'<ul class="placeholder"><li>place holder</li></ul>') |
148 w(u'<ul class="placeholder"><li>place holder</li></ul>') |
|
149 # the local node info |
|
150 self.wview(vid, self.rset, row=row, col=col) |
125 self.wview(vid, self.rset, row=row, col=col) |
151 if is_open: # recurse if needed |
126 self.w(u'</li>') |
152 self.wview(parentvid, self.req.execute(rql), treeid=treeid, initial_load=False) |
|
153 w(u'</li>') |
|
154 |
127 |
155 |
|
156 @monkeypatch(JSonController) |
|
157 def js_node_clicked(self, treeid, nodeeid): |
|
158 """add/remove eid in treestate cookie""" |
|
159 cookies = self.req.get_cookie() |
|
160 statename = treecookiename(treeid) |
|
161 treestate = cookies.get(statename) |
|
162 if treestate is None: |
|
163 cookies[statename] = nodeeid |
|
164 self.req.set_cookie(cookies, statename) |
|
165 else: |
|
166 marked = set(filter(None, treestate.value.split(';'))) |
|
167 if nodeeid in marked: |
|
168 marked.remove(nodeeid) |
|
169 else: |
|
170 marked.add(nodeeid) |
|
171 cookies[statename] = ';'.join(marked) |
|
172 self.req.set_cookie(cookies, statename) |
|