13 from cubicweb import NoSelectableObject, role |
13 from cubicweb import NoSelectableObject, role |
14 from cubicweb.selectors import partial_has_related_entities |
14 from cubicweb.selectors import partial_has_related_entities |
15 from cubicweb.view import EntityView |
15 from cubicweb.view import EntityView |
16 from cubicweb.common import tags, uilib |
16 from cubicweb.common import tags, uilib |
17 from cubicweb.utils import make_uid |
17 from cubicweb.utils import make_uid |
|
18 from cubicweb.web.views import primary |
18 |
19 |
19 class LazyViewMixin(object): |
20 class LazyViewMixin(object): |
20 """provides two convenience methods for the tab machinery |
21 """provides two convenience methods for the tab machinery |
21 can also be used to lazy-load arbitrary views |
22 can also be used to lazy-load arbitrary views |
22 caveat : lazyview is not recursive, i.e : you can't (successfully) |
23 caveat : lazyview is not recursive, i.e : you can't (successfully) |
28 jQuery('#lazy-%(vid)s').bind('%(event)s', function(event) { |
29 jQuery('#lazy-%(vid)s').bind('%(event)s', function(event) { |
29 load_now('#lazy-%(vid)s', '#%(vid)s-hole', %(reloadable)s); |
30 load_now('#lazy-%(vid)s', '#%(vid)s-hole', %(reloadable)s); |
30 });""" % {'event': 'load_%s' % vid, 'vid': vid, |
31 });""" % {'event': 'load_%s' % vid, 'vid': vid, |
31 'reloadable' : str(reloadable).lower()}) |
32 'reloadable' : str(reloadable).lower()}) |
32 |
33 |
33 def lazyview(self, vid, rql=None, eid=None, rset=None, static=False, |
34 def lazyview(self, vid, rql=None, eid=None, rset=None, tabid=None, |
34 reloadable=False, show_spinbox=True, w=None): |
35 reloadable=False, show_spinbox=True, w=None): |
35 """a lazy version of wview |
36 """a lazy version of wview |
36 first version only support lazy viewing for an entity at a time |
37 first version only support lazy viewing for an entity at a time |
37 """ |
38 """ |
38 assert rql or eid or rset or static, \ |
|
39 'lazyview wants at least : rql, or an eid, or an rset -- or call it with static=True' |
|
40 w = w or self.w |
39 w = w or self.w |
41 self._cw.add_js('cubicweb.lazy.js') |
40 self._cw.add_js('cubicweb.lazy.js') |
42 urlparams = {'vid' : vid, 'fname' : 'view'} |
41 urlparams = {'vid' : vid, 'fname' : 'view'} |
43 if rql: |
42 if rql: |
44 urlparams['rql'] = rql |
43 urlparams['rql'] = rql |
45 elif eid: |
44 elif eid: |
46 urlparams['rql'] = uilib.rql_for_eid(eid) |
45 urlparams['rql'] = uilib.rql_for_eid(eid) |
47 elif rset: |
46 elif rset: |
48 urlparams['rql'] = rset.printable_rql() |
47 urlparams['rql'] = rset.printable_rql() |
49 w(u'<div id="lazy-%s" cubicweb:loadurl="%s">' % ( |
48 w(u'<div id="lazy-%s" cubicweb:loadurl="%s">' % ( |
50 vid, xml_escape(self._cw.build_url('json', **urlparams)))) |
49 tabid or vid, xml_escape(self._cw.build_url('json', **urlparams)))) |
51 if show_spinbox: |
50 if show_spinbox: |
52 w(u'<img src="data/loading.gif" id="%s-hole" alt="%s"/>' |
51 w(u'<img src="data/loading.gif" id="%s-hole" alt="%s"/>' |
53 % (vid, self._cw._('loading'))) |
52 % (tabid or vid, self._cw._('loading'))) |
54 w(u'</div>') |
53 w(u'</div>') |
55 self._prepare_bindings(vid, reloadable) |
54 self._prepare_bindings(tabid or vid, reloadable) |
56 |
55 |
57 def forceview(self, vid): |
56 def forceview(self, vid): |
58 """trigger an event that will force immediate loading of the view |
57 """trigger an event that will force immediate loading of the view |
59 on dom readyness |
58 on dom readyness |
60 """ |
59 """ |
68 |
67 |
69 @property |
68 @property |
70 def cookie_name(self): |
69 def cookie_name(self): |
71 return str('%s_active_tab' % self._cw.config.appid) |
70 return str('%s_active_tab' % self._cw.config.appid) |
72 |
71 |
73 def active_tab(self, tabs, default): |
72 def active_tab(self, default): |
74 formtab = self._cw.form.get('tab') |
73 if 'tab' in self._cw.form: |
75 if formtab in tabs: |
74 return self._cw.form['tab'] |
76 return formtab |
|
77 cookies = self._cw.get_cookie() |
75 cookies = self._cw.get_cookie() |
78 cookiename = self.cookie_name |
76 cookiename = self.cookie_name |
79 activetab = cookies.get(cookiename) |
77 activetab = cookies.get(cookiename) |
80 if activetab is None: |
78 if activetab is None: |
81 cookies[cookiename] = default |
79 cookies[cookiename] = default |
82 self._cw.set_cookie(cookies, cookiename) |
80 self._cw.set_cookie(cookies, cookiename) |
83 tab = default |
81 return default |
84 else: |
82 return activetab.value |
85 tab = activetab.value |
83 |
86 return tab in tabs and tab or default |
84 def prune_tabs(self, tabs, default_tab): |
87 |
|
88 def prune_tabs(self, tabs): |
|
89 selected_tabs = [] |
85 selected_tabs = [] |
|
86 may_be_active_tab = self.active_tab(default_tab) |
|
87 active_tab = default_tab |
|
88 viewsvreg = self._cw.vreg['views'] |
90 for tab in tabs: |
89 for tab in tabs: |
91 try: |
90 try: |
92 self._cw.vreg['views'].select(tab, self._cw, rset=self.cw_rset) |
91 tabid, tabkwargs = tab |
93 selected_tabs.append(tab) |
92 tabkwargs = tabkwargs.copy() |
|
93 except ValueError: |
|
94 tabid, tabkwargs = tab, {} |
|
95 tabkwargs.setdefault('rset', self.rset) |
|
96 vid = tabkwargs.get('vid', tabid) |
|
97 try: |
|
98 viewsvreg.select(vid, self._cw, **tabkwargs) |
|
99 selected_tabs.append((tabid, tabkwargs)) |
94 except NoSelectableObject: |
100 except NoSelectableObject: |
95 continue |
101 continue |
96 return selected_tabs |
102 if tabid == may_be_active_tab: |
|
103 active_tab = tabid |
|
104 return selected_tabs, active_tab |
97 |
105 |
98 def render_tabs(self, tabs, default, entity=None): |
106 def render_tabs(self, tabs, default, entity=None): |
99 # delegate to the default tab if there is more than one entity |
107 # delegate to the default tab if there is more than one entity |
100 # in the result set (tabs are pretty useless there) |
108 # in the result set (tabs are pretty useless there) |
101 if entity and len(self.cw_rset) > 1: |
109 if entity and len(self.cw_rset) > 1: |
103 return |
111 return |
104 self._cw.add_css('ui.tabs.css') |
112 self._cw.add_css('ui.tabs.css') |
105 self._cw.add_js(('ui.core.js', 'ui.tabs.js', |
113 self._cw.add_js(('ui.core.js', 'ui.tabs.js', |
106 'cubicweb.ajax.js', 'cubicweb.tabs.js', 'cubicweb.lazy.js')) |
114 'cubicweb.ajax.js', 'cubicweb.tabs.js', 'cubicweb.lazy.js')) |
107 # prune tabs : not all are to be shown |
115 # prune tabs : not all are to be shown |
108 tabs = self.prune_tabs(tabs) |
116 tabs, active_tab = self.prune_tabs(tabs, default) |
109 # select a tab |
|
110 active_tab = self.active_tab(tabs, default) |
|
111 # build the html structure |
117 # build the html structure |
112 w = self.w |
118 w = self.w |
113 uid = entity and entity.eid or make_uid('tab') |
119 uid = entity and entity.eid or make_uid('tab') |
114 w(u'<div id="entity-tabs-%s">' % uid) |
120 w(u'<div id="entity-tabs-%s">' % uid) |
115 w(u'<ul>') |
121 w(u'<ul>') |
116 for tab in tabs: |
122 active_tab_idx = None |
|
123 for i, (tabid, tabkwargs) in enumerate(tabs): |
117 w(u'<li>') |
124 w(u'<li>') |
118 w(u'<a href="#%s">' % tab) |
125 w(u'<a href="#%s">' % tabid) |
119 w(u'<span onclick="set_tab(\'%s\', \'%s\')">' % (tab, self.cookie_name)) |
126 w(u'<span onclick="set_tab(\'%s\', \'%s\')">' % (tabid, self.cookie_name)) |
120 w(self._cw._(tab)) |
127 w(tabkwargs.pop('label', self._cw._(tabid))) |
121 w(u'</span>') |
128 w(u'</span>') |
122 w(u'</a>') |
129 w(u'</a>') |
123 w(u'</li>') |
130 w(u'</li>') |
|
131 if tabid == active_tab: |
|
132 active_tab_idx = i |
124 w(u'</ul>') |
133 w(u'</ul>') |
125 w(u'</div>') |
134 w(u'</div>') |
126 for tab in tabs: |
135 for tabid, tabkwargs in tabs: |
127 w(u'<div id="%s">' % tab) |
136 w(u'<div id="%s">' % tabid) |
128 if entity: |
137 tabkwargs.setdefault('tabid', tabid) |
129 self.lazyview(tab, eid=entity.eid) |
138 tabkwargs.setdefault('vid', tabid) |
130 else: |
139 tabkwargs.setdefault('rset', self.rset) |
131 self.lazyview(tab, static=True) |
140 self.lazyview(**tabkwargs) |
132 w(u'</div>') |
141 w(u'</div>') |
133 # call the set_tab() JS function *after* each tab is generated |
142 # call the set_tab() JS function *after* each tab is generated |
134 # because the callback binding needs to be done before |
143 # because the callback binding needs to be done before |
135 # XXX make work history: true |
144 # XXX make work history: true |
136 self._cw.add_onload(u""" |
145 self._cw.add_onload(u""" |
137 jQuery('#entity-tabs-%(eeid)s > ul').tabs( { selected: %(tabindex)s }); |
146 jQuery('#entity-tabs-%(eeid)s > ul').tabs( { selected: %(tabindex)s }); |
138 set_tab('%(vid)s', '%(cookiename)s'); |
147 set_tab('%(vid)s', '%(cookiename)s'); |
139 """ % {'tabindex' : tabs.index(active_tab), |
148 """ % {'tabindex' : active_tab_idx, |
140 'vid' : active_tab, |
149 'vid' : active_tab, |
141 'eeid' : (entity and entity.eid or uid), |
150 'eeid' : (entity and entity.eid or uid), |
142 'cookiename' : self.cookie_name}) |
151 'cookiename' : self.cookie_name}) |
143 |
152 |
144 |
153 |
168 self.w(u'<div class="mainInfo">') |
177 self.w(u'<div class="mainInfo">') |
169 if self.title: |
178 if self.title: |
170 self.w(tags.h1(self._cw._(self.title))) |
179 self.w(tags.h1(self._cw._(self.title))) |
171 self.wview(self.vid, rset, 'noresult') |
180 self.wview(self.vid, rset, 'noresult') |
172 self.w(u'</div>') |
181 self.w(u'</div>') |
|
182 |
|
183 |
|
184 class TabedPrimaryView(TabsMixin, primary.PrimaryView): |
|
185 __abstract__ = True # don't register |
|
186 |
|
187 tabs = ['main_tab'] |
|
188 default_tab = 'main_tab' |
|
189 |
|
190 def cell_call(self, row, col): |
|
191 entity = self.complete_entity(row, col) |
|
192 self.render_entity_title(entity) |
|
193 self.render_entity_metadata(entity) |
|
194 self.render_tabs(self.tabs, self.default_tab, entity) |
|
195 |
|
196 |
|
197 class PrimaryTab(primary.PrimaryView): |
|
198 id = 'main_tab' |
|
199 title = None |
|
200 |
|
201 def is_primary(self): |
|
202 return True |
|
203 |
|
204 def render_entity_title(self, entity): |
|
205 pass |
|
206 |
|
207 def render_entity_metadata(self, entity): |
|
208 pass |
|
209 |