16 # You should have received a copy of the GNU Lesser General Public License along |
16 # You should have received a copy of the GNU Lesser General Public License along |
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
18 """default templates for CubicWeb web client""" |
18 """default templates for CubicWeb web client""" |
19 |
19 |
20 |
20 |
21 from cubicweb import _ |
|
22 |
|
23 from logilab.mtconverter import xml_escape |
21 from logilab.mtconverter import xml_escape |
24 from logilab.common.deprecation import class_renamed |
22 from logilab.common.deprecation import class_renamed |
25 from logilab.common.registry import objectify_predicate |
23 from logilab.common.registry import objectify_predicate |
26 from logilab.common.decorators import classproperty |
24 from logilab.common.decorators import classproperty |
27 |
25 |
28 from cubicweb.predicates import match_kwargs, no_cnx, anonymous_user |
26 from cubicweb import _ |
|
27 from cubicweb.predicates import match_kwargs, anonymous_user |
29 from cubicweb.view import View, MainTemplate, NOINDEX, NOFOLLOW, StartupView |
28 from cubicweb.view import View, MainTemplate, NOINDEX, NOFOLLOW, StartupView |
30 from cubicweb.utils import UStringIO |
29 from cubicweb.utils import UStringIO |
31 from cubicweb.schema import display_name |
30 from cubicweb.schema import display_name |
32 from cubicweb.web import component, formfields as ff, formwidgets as fw |
31 from cubicweb.web import formfields as ff, formwidgets as fw |
33 from cubicweb.web.views import forms |
32 from cubicweb.web.views import forms |
|
33 |
34 |
34 |
35 # main templates ############################################################## |
35 # main templates ############################################################## |
36 |
36 |
37 class LogInOutTemplate(MainTemplate): |
37 class LogInOutTemplate(MainTemplate): |
38 |
38 |
89 |
89 |
90 @objectify_predicate |
90 @objectify_predicate |
91 def modal_view(cls, req, rset, *args, **kwargs): |
91 def modal_view(cls, req, rset, *args, **kwargs): |
92 if req.form.get('__modal', None): |
92 if req.form.get('__modal', None): |
93 return 1 |
93 return 1 |
|
94 |
94 |
95 |
95 @objectify_predicate |
96 @objectify_predicate |
96 def templatable_view(cls, req, rset, *args, **kwargs): |
97 def templatable_view(cls, req, rset, *args, **kwargs): |
97 view = kwargs.pop('view', None) |
98 view = kwargs.pop('view', None) |
98 if view is None: |
99 if view is None: |
174 self.template_html_header(content_type, page_title, additional_headers) |
175 self.template_html_header(content_type, page_title, additional_headers) |
175 self.template_body_header(view) |
176 self.template_body_header(view) |
176 |
177 |
177 def template_html_header(self, content_type, page_title, additional_headers=()): |
178 def template_html_header(self, content_type, page_title, additional_headers=()): |
178 w = self.whead |
179 w = self.whead |
179 lang = self._cw.lang |
|
180 self.write_doctype() |
180 self.write_doctype() |
181 self._cw.html_headers.define_var('BASE_URL', self._cw.base_url()) |
181 self._cw.html_headers.define_var('BASE_URL', self._cw.base_url()) |
182 self._cw.html_headers.define_var('DATA_URL', self._cw.datadir_url) |
182 self._cw.html_headers.define_var('DATA_URL', self._cw.datadir_url) |
183 w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n' |
183 w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n' |
184 % (content_type, self._cw.encoding)) |
184 % (content_type, self._cw.encoding)) |
213 |
213 |
214 def nav_column(self, view, context): |
214 def nav_column(self, view, context): |
215 boxes = list(self._cw.vreg['ctxcomponents'].poss_visible_objects( |
215 boxes = list(self._cw.vreg['ctxcomponents'].poss_visible_objects( |
216 self._cw, rset=self.cw_rset, view=view, context=context)) |
216 self._cw, rset=self.cw_rset, view=view, context=context)) |
217 if boxes: |
217 if boxes: |
218 getlayout = self._cw.vreg['components'].select |
|
219 self.w(u'<td id="navColumn%s"><div class="navboxes">\n' % context.capitalize()) |
218 self.w(u'<td id="navColumn%s"><div class="navboxes">\n' % context.capitalize()) |
220 for box in boxes: |
219 for box in boxes: |
221 box.render(w=self.w, view=view) |
220 box.render(w=self.w, view=view) |
222 self.w(u'</div></td>\n') |
221 self.w(u'</div></td>\n') |
223 |
222 |
246 view.render(w=self.w) |
245 view.render(w=self.w) |
247 self.template_footer(view) |
246 self.template_footer(view) |
248 |
247 |
249 def template_header(self, content_type, view=None, page_title='', additional_headers=()): |
248 def template_header(self, content_type, view=None, page_title='', additional_headers=()): |
250 w = self.whead |
249 w = self.whead |
251 lang = self._cw.lang |
|
252 self.write_doctype() |
250 self.write_doctype() |
253 w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n' |
251 w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n' |
254 % (content_type, self._cw.encoding)) |
252 % (content_type, self._cw.encoding)) |
255 w(u'\n'.join(additional_headers)) |
253 w(u'\n'.join(additional_headers)) |
256 self.wview('htmlheader', rset=self.cw_rset) |
254 self.wview('htmlheader', rset=self.cw_rset) |
267 |
265 |
268 def template_header(self, content_type, view=None, page_title='', additional_headers=()): |
266 def template_header(self, content_type, view=None, page_title='', additional_headers=()): |
269 page_title = page_title or view.page_title() |
267 page_title = page_title or view.page_title() |
270 additional_headers = additional_headers or view.html_headers() |
268 additional_headers = additional_headers or view.html_headers() |
271 whead = self.whead |
269 whead = self.whead |
272 lang = self._cw.lang |
|
273 self.write_doctype() |
270 self.write_doctype() |
274 whead(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n' |
271 whead(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n' |
275 % (content_type, self._cw.encoding)) |
272 % (content_type, self._cw.encoding)) |
276 whead(u'\n'.join(additional_headers) + u'\n') |
273 whead(u'\n'.join(additional_headers) + u'\n') |
277 self.wview('htmlheader', rset=self.cw_rset) |
274 self.wview('htmlheader', rset=self.cw_rset) |
335 for jscript in self._cw.uiprops['JAVASCRIPTS']: |
332 for jscript in self._cw.uiprops['JAVASCRIPTS']: |
336 self._cw.add_js(jscript, localfile=False) |
333 self._cw.add_js(jscript, localfile=False) |
337 |
334 |
338 def alternates(self): |
335 def alternates(self): |
339 urlgetter = self._cw.vreg['components'].select_or_none('rss_feed_url', |
336 urlgetter = self._cw.vreg['components'].select_or_none('rss_feed_url', |
340 self._cw, rset=self.cw_rset) |
337 self._cw, rset=self.cw_rset) |
341 if urlgetter is not None: |
338 if urlgetter is not None: |
342 self.whead(u'<link rel="alternate" type="application/rss+xml" title="RSS feed" href="%s"/>\n' |
339 self.whead(u'<link rel="alternate" type="application/rss+xml" title="RSS feed" href="%s"/>\n' |
343 % xml_escape(urlgetter.feed_url())) |
340 % xml_escape(urlgetter.feed_url())) |
344 |
341 |
345 |
342 |
346 class HTMLPageHeader(View): |
343 class HTMLPageHeader(View): |
347 """default html page header""" |
344 """default html page header""" |
348 __regid__ = 'header' |
345 __regid__ = 'header' |
404 self.w(u'<a href="%s">%s</a>' % (action.url(), |
401 self.w(u'<a href="%s">%s</a>' % (action.url(), |
405 self._cw._(action.title))) |
402 self._cw._(action.title))) |
406 if i < (len(footeractions) - 1): |
403 if i < (len(footeractions) - 1): |
407 self.w(u' | ') |
404 self.w(u' | ') |
408 |
405 |
|
406 |
409 class HTMLContentHeader(View): |
407 class HTMLContentHeader(View): |
410 """default html page content header: |
408 """default html page content header: |
411 * include message component if selectable for this request |
409 * include message component if selectable for this request |
412 * include selectable content navigation components |
410 * include selectable content navigation components |
413 """ |
411 """ |
436 if components: |
434 if components: |
437 self.w(u'<div id="contentfooter">') |
435 self.w(u'<div id="contentfooter">') |
438 for comp in components: |
436 for comp in components: |
439 comp.render(w=self.w, view=view) |
437 comp.render(w=self.w, view=view) |
440 self.w(u'</div>') |
438 self.w(u'</div>') |
|
439 |
441 |
440 |
442 class BaseLogForm(forms.FieldsForm): |
441 class BaseLogForm(forms.FieldsForm): |
443 """Abstract Base login form to be used by any login form |
442 """Abstract Base login form to be used by any login form |
444 """ |
443 """ |
445 __abstract__ = True |
444 __abstract__ = True |
459 form_buttons = [fw.SubmitButton(label=_('log in'), |
458 form_buttons = [fw.SubmitButton(label=_('log in'), |
460 attrs={'class': 'loginButton'}), |
459 attrs={'class': 'loginButton'}), |
461 fw.ResetButton(label=_('cancel'), |
460 fw.ResetButton(label=_('cancel'), |
462 attrs={'class': 'loginButton', |
461 attrs={'class': 'loginButton', |
463 'onclick': onclick}),] |
462 'onclick': onclick}),] |
464 ## Can't shortcut next access because __dict__ is a "dictproxy" which |
463 ## Can't shortcut next access because __dict__ is a "dictproxy" which |
465 ## does not support items assignement. |
464 ## does not support items assignement. |
466 # cls.__dict__['form_buttons'] = form_buttons |
465 # cls.__dict__['form_buttons'] = form_buttons |
467 return form_buttons |
466 return form_buttons |
468 |
467 |
469 def form_action(self): |
468 def form_action(self): |
475 if target and target != '/': |
474 if target and target != '/': |
476 url_args['postlogin_path'] = target |
475 url_args['postlogin_path'] = target |
477 return self._cw.build_url('login', __secure__=True, **url_args) |
476 return self._cw.build_url('login', __secure__=True, **url_args) |
478 return super(BaseLogForm, self).form_action() |
477 return super(BaseLogForm, self).form_action() |
479 |
478 |
|
479 |
480 class LogForm(BaseLogForm): |
480 class LogForm(BaseLogForm): |
481 """Simple login form that send username and password |
481 """Simple login form that send username and password |
482 """ |
482 """ |
483 __regid__ = 'logform' |
483 __regid__ = 'logform' |
484 domid = 'loginForm' |
484 domid = 'loginForm' |
486 # XXX have to recall fields name since python is mangling __login/__password |
486 # XXX have to recall fields name since python is mangling __login/__password |
487 __login = ff.StringField('__login', widget=fw.TextInput({'class': 'data'})) |
487 __login = ff.StringField('__login', widget=fw.TextInput({'class': 'data'})) |
488 __password = ff.StringField('__password', label=_('password'), |
488 __password = ff.StringField('__password', label=_('password'), |
489 widget=fw.PasswordSingleInput({'class': 'data'})) |
489 widget=fw.PasswordSingleInput({'class': 'data'})) |
490 |
490 |
491 onclick_args = ('popupLoginBox', '__login') |
491 onclick_args = ('popupLoginBox', '__login') |
492 |
492 |
493 |
493 |
494 class LogFormView(View): |
494 class LogFormView(View): |
495 # XXX an awful lot of hardcoded assumptions there |
495 # XXX an awful lot of hardcoded assumptions there |
496 # makes it unobvious to reuse/specialize |
496 # makes it unobvious to reuse/specialize |
529 label = cw.pgettext('CWUser', 'login') |
529 label = cw.pgettext('CWUser', 'login') |
530 form.field_by_name('__login').label = label |
530 form.field_by_name('__login').label = label |
531 form.render(w=self.w, table_class='', display_progress_div=False) |
531 form.render(w=self.w, table_class='', display_progress_div=False) |
532 cw.html_headers.add_onload('jQuery("#__login:visible").focus()') |
532 cw.html_headers.add_onload('jQuery("#__login:visible").focus()') |
533 |
533 |
|
534 |
534 LogFormTemplate = class_renamed('LogFormTemplate', LogFormView) |
535 LogFormTemplate = class_renamed('LogFormTemplate', LogFormView) |