diff -r 058bb3dc685f -r 0b59724cb3f2 web/component.py --- a/web/component.py Mon Jan 04 18:40:30 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,752 +0,0 @@ -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of CubicWeb. -# -# CubicWeb is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# CubicWeb is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with CubicWeb. If not, see . -"""abstract component class and base components definition for CubicWeb web -client -""" - -__docformat__ = "restructuredtext en" -from cubicweb import _ - -from warnings import warn - -from six import PY3, add_metaclass, text_type - -from logilab.common.deprecation import class_deprecated, class_renamed, deprecated -from logilab.mtconverter import xml_escape - -from cubicweb import Unauthorized, role, target, tags -from cubicweb.schema import display_name -from cubicweb.uilib import js, domid -from cubicweb.utils import json_dumps, js_href -from cubicweb.view import ReloadableMixIn, Component -from cubicweb.predicates import (no_cnx, paginated_rset, one_line_rset, - non_final_entity, partial_relation_possible, - partial_has_related_entities) -from cubicweb.appobject import AppObject -from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs - - -# abstract base class for navigation components ################################ - -class NavigationComponent(Component): - """abstract base class for navigation components""" - __regid__ = 'navigation' - __select__ = paginated_rset() - - cw_property_defs = { - _('visible'): dict(type='Boolean', default=True, - help=_('display the component or not')), - } - - page_size_property = 'navigation.page-size' - start_param = '__start' - stop_param = '__stop' - page_link_templ = u'%s' - selected_page_link_templ = u'%s' - previous_page_link_templ = next_page_link_templ = page_link_templ - - def __init__(self, req, rset, **kwargs): - super(NavigationComponent, self).__init__(req, rset=rset, **kwargs) - self.starting_from = 0 - self.total = rset.rowcount - - def get_page_size(self): - try: - return self._page_size - except AttributeError: - page_size = self.cw_extra_kwargs.get('page_size') - if page_size is None: - if 'page_size' in self._cw.form: - page_size = int(self._cw.form['page_size']) - else: - page_size = self._cw.property_value(self.page_size_property) - self._page_size = page_size - return page_size - - def set_page_size(self, page_size): - self._page_size = page_size - - page_size = property(get_page_size, set_page_size) - - def page_boundaries(self): - try: - stop = int(self._cw.form[self.stop_param]) + 1 - start = int(self._cw.form[self.start_param]) - except KeyError: - start, stop = 0, self.page_size - if start >= len(self.cw_rset): - start, stop = 0, self.page_size - self.starting_from = start - return start, stop - - def clean_params(self, params): - if self.start_param in params: - del params[self.start_param] - if self.stop_param in params: - del params[self.stop_param] - - def page_url(self, path, params, start=None, stop=None): - params = dict(params) - params['__fromnavigation'] = 1 - if start is not None: - params[self.start_param] = start - if stop is not None: - params[self.stop_param] = stop - view = self.cw_extra_kwargs.get('view') - if view is not None and hasattr(view, 'page_navigation_url'): - url = view.page_navigation_url(self, path, params) - elif path in ('json', 'ajax'): - # 'ajax' is the new correct controller, but the old 'json' - # controller should still be supported - url = self.ajax_page_url(**params) - else: - url = self._cw.build_url(path, **params) - # XXX hack to avoid opening a new page containing the evaluation of the - # js expression on ajax call - if url.startswith('javascript:'): - url += '; $.noop();' - return url - - def ajax_page_url(self, **params): - divid = params.setdefault('divid', 'pageContent') - params['rql'] = self.cw_rset.printable_rql() - return js_href("$(%s).loadxhtml(AJAX_PREFIX_URL, %s, 'get', 'swap')" % ( - json_dumps('#'+divid), js.ajaxFuncArgs('view', params))) - - def page_link(self, path, params, start, stop, content): - url = xml_escape(self.page_url(path, params, start, stop)) - if start == self.starting_from: - return self.selected_page_link_templ % (url, content, content) - return self.page_link_templ % (url, content, content) - - @property - def prev_icon_url(self): - return xml_escape(self._cw.data_url('go_prev.png')) - - @property - def next_icon_url(self): - return xml_escape(self._cw.data_url('go_next.png')) - - @property - def no_previous_page_link(self): - return (u'%s' % - (self.prev_icon_url, self._cw._('there is no previous page'))) - - @property - def no_next_page_link(self): - return (u'%s' % - (self.next_icon_url, self._cw._('there is no next page'))) - - @property - def no_content_prev_link(self): - return (u'%s' % ( - (self.prev_icon_url, self._cw._('no content prev link')))) - - @property - def no_content_next_link(self): - return (u'%s' % - (self.next_icon_url, self._cw._('no content next link'))) - - def previous_link(self, path, params, content=None, title=_('previous_results')): - if not content: - content = self.no_content_prev_link - start = self.starting_from - if not start : - return self.no_previous_page_link - start = max(0, start - self.page_size) - stop = start + self.page_size - 1 - url = xml_escape(self.page_url(path, params, start, stop)) - return self.previous_page_link_templ % (url, self._cw._(title), content) - - def next_link(self, path, params, content=None, title=_('next_results')): - if not content: - content = self.no_content_next_link - start = self.starting_from + self.page_size - if start >= self.total: - return self.no_next_page_link - stop = start + self.page_size - 1 - url = xml_escape(self.page_url(path, params, start, stop)) - return self.next_page_link_templ % (url, self._cw._(title), content) - - -# new contextual components system ############################################# - -def override_ctx(cls, **kwargs): - cwpdefs = cls.cw_property_defs.copy() - cwpdefs['context'] = cwpdefs['context'].copy() - cwpdefs['context'].update(kwargs) - return cwpdefs - - -class EmptyComponent(Exception): - """some selectable component has actually no content and should not be - rendered - """ - - -class Link(object): - """a link to a view or action in the ui. - - Use this rather than `cw.web.htmlwidgets.BoxLink`. - - Note this class could probably be avoided with a proper DOM on the server - side. - """ - newstyle = True - - def __init__(self, href, label, **attrs): - self.href = href - self.label = label - self.attrs = attrs - - def __unicode__(self): - return tags.a(self.label, href=self.href, **self.attrs) - - if PY3: - __str__ = __unicode__ - - def render(self, w): - w(tags.a(self.label, href=self.href, **self.attrs)) - - def __repr__(self): - return '<%s: href=%r label=%r %r>' % (self.__class__.__name__, - self.href, self.label, self.attrs) - - -class Separator(object): - """a menu separator. - - Use this rather than `cw.web.htmlwidgets.BoxSeparator`. - """ - newstyle = True - - def render(self, w): - w(u'
') - - -def _bwcompatible_render_item(w, item): - if hasattr(item, 'render'): - if getattr(item, 'newstyle', False): - if isinstance(item, Separator): - w(u'') - item.render(w) - w(u'