[web api] unify 'contentnav' (VComponent) and 'boxes' registries as 'ctxcomponents' (CtxComponent)
--- a/cwvreg.py Wed Aug 25 10:01:11 2010 +0200
+++ b/cwvreg.py Wed Aug 25 10:29:07 2010 +0200
@@ -421,6 +421,44 @@
VRegistry.REGISTRY_FACTORY['actions'] = ActionsRegistry
+class CtxComponentsRegistry(CWRegistry):
+ def poss_visible_objects(self, *args, **kwargs):
+ """return an ordered list of possible components"""
+ context = kwargs.pop('context')
+ if kwargs.get('rset') is None:
+ cache = args[0]
+ else:
+ cache = kwargs['rset']
+ try:
+ cached = cache.__components_cache
+ except AttributeError:
+ ctxcomps = super(CtxComponentsRegistry, self).poss_visible_objects(
+ *args, **kwargs)
+ cached = cache.__components_cache = {}
+ for component in ctxcomps:
+ cached.setdefault(component.cw_propval('context'), []).append(component)
+ thisctxcomps = cached.get(context, ())
+ # XXX set context for bw compat (should now be taken by comp.render())
+ for component in thisctxcomps:
+ component.cw_extra_kwargs['context'] = context
+ return thisctxcomps
+
+VRegistry.REGISTRY_FACTORY['ctxcomponents'] = CtxComponentsRegistry
+
+
+class BwCompatCWRegistry(object):
+ def __init__(self, vreg, oldreg, redirecttoreg):
+ self.vreg = vreg
+ self.oldreg = oldreg
+ self.redirecto = redirecttoreg
+
+ def __getattr__(self, attr):
+ warn('[3.10] you should now use the %s registry instead of the %s registry'
+ % (self.redirecto, self.oldreg), DeprecationWarning, stacklevel=2)
+ return getattr(self.vreg[self.redirecto], attr)
+
+ def clear(self): pass
+ def initialization_completed(self): pass
class CubicWebVRegistry(VRegistry):
"""Central registry for the cubicweb instance, extending the generic
@@ -433,15 +471,23 @@
stored objects. Currently we have the following registries of objects known
by the web instance (library may use some others additional registries):
- * etypes
- * views
- * components
- * actions
- * forms
- * formrenderers
- * controllers, which are directly plugged into the application
- object to handle request publishing XXX to merge with views
- * contentnavigation XXX to merge with components? to kill?
+ * 'etypes', entity type classes
+
+ * 'views', views and templates (e.g. layout views)
+
+ * 'components', non contextual components, like magic search, url evaluators
+
+ * 'ctxcomponents', contextual components like boxes and dynamic section
+
+ * 'actions', contextual actions, eg links to display in predefined places in
+ the ui
+
+ * 'forms', describing logic of HTML form
+
+ * 'formrenderers', rendering forms to html
+
+ * 'controllers', primary objects to handle request publishing, directly
+ plugged into the application
"""
def __init__(self, config, initlog=True):
@@ -456,6 +502,8 @@
# don't clear rtags during test, this may cause breakage with
# manually imported appobject modules
CW_EVENT_MANAGER.bind('before-registry-reload', clear_rtag_objects)
+ self['boxes'] = BwCompatCWRegistry(self, 'boxes', 'ctxcomponents')
+ self['contentnavigation'] = BwCompatCWRegistry(self, 'contentnavigation', 'ctxcomponents')
def setdefault(self, regid):
try:
@@ -751,7 +799,7 @@
def possible_actions(self, req, rset=None, **kwargs):
return self["actions"].possible_actions(req, rest=rset, **kwargs)
- @deprecated('[3.4] use vreg["boxes"].select_object(...)')
+ @deprecated('[3.4] use vreg["ctxcomponents"].select_object(...)')
def select_box(self, oid, *args, **kwargs):
return self['boxes'].select_object(oid, *args, **kwargs)
--- a/devtools/testlib.py Wed Aug 25 10:01:11 2010 +0200
+++ b/devtools/testlib.py Wed Aug 25 10:29:07 2010 +0200
@@ -475,7 +475,7 @@
def list_boxes_for(self, rset):
"""returns the list of boxes that can be applied on `rset`"""
req = rset.req
- for box in self.vreg['boxes'].possible_objects(req, rset=rset):
+ for box in self.vreg['ctxcomponents'].possible_objects(req, rset=rset):
yield box
def list_startup_views(self):
--- a/i18n/en.po Wed Aug 25 10:01:11 2010 +0200
+++ b/i18n/en.po Wed Aug 25 10:29:07 2010 +0200
@@ -1119,52 +1119,52 @@
msgid "boxes"
msgstr ""
-msgid "boxes_bookmarks_box"
+msgid "ctxcomponents_bookmarks_box"
msgstr "bookmarks box"
-msgid "boxes_bookmarks_box_description"
+msgid "ctxcomponents_bookmarks_box_description"
msgstr "box listing the user's bookmarks"
-msgid "boxes_download_box"
+msgid "ctxcomponents_download_box"
msgstr "download box"
-msgid "boxes_download_box_description"
-msgstr ""
-
-msgid "boxes_edit_box"
+msgid "ctxcomponents_download_box_description"
+msgstr ""
+
+msgid "ctxcomponents_edit_box"
msgstr "actions box"
-msgid "boxes_edit_box_description"
+msgid "ctxcomponents_edit_box_description"
msgstr "box listing the applicable actions on the displayed data"
-msgid "boxes_filter_box"
+msgid "ctxcomponents_filter_box"
msgstr "filter"
-msgid "boxes_filter_box_description"
+msgid "ctxcomponents_filter_box_description"
msgstr "box providing filter within current search results functionality"
-msgid "boxes_possible_views_box"
+msgid "ctxcomponents_possible_views_box"
msgstr "possible views box"
-msgid "boxes_possible_views_box_description"
+msgid "ctxcomponents_possible_views_box_description"
msgstr "box listing the possible views for the displayed data"
-msgid "boxes_rss"
+msgid "ctxcomponents_rss"
msgstr "rss box"
-msgid "boxes_rss_description"
+msgid "ctxcomponents_rss_description"
msgstr "RSS icon to get displayed data as a RSS thread"
-msgid "boxes_search_box"
+msgid "ctxcomponents_search_box"
msgstr "search box"
-msgid "boxes_search_box_description"
+msgid "ctxcomponents_search_box_description"
msgstr "search box"
-msgid "boxes_startup_views_box"
+msgid "ctxcomponents_startup_views_box"
msgstr "startup views box"
-msgid "boxes_startup_views_box_description"
+msgid "ctxcomponents_startup_views_box_description"
msgstr "box listing the possible start pages"
msgid "bug report sent"
@@ -1440,41 +1440,41 @@
msgid "content type"
msgstr ""
-msgid "contentnavigation"
+msgid "ctxcomponents"
msgstr "contextual components"
-msgid "contentnavigation_breadcrumbs"
+msgid "ctxcomponents_breadcrumbs"
msgstr "breadcrumb"
-msgid "contentnavigation_breadcrumbs_description"
+msgid "ctxcomponents_breadcrumbs_description"
msgstr "breadcrumbs bar that display a path locating the page in the site"
-msgid "contentnavigation_metadata"
+msgid "ctxcomponents_metadata"
msgstr "entity's metadata"
-msgid "contentnavigation_metadata_description"
-msgstr ""
-
-msgid "contentnavigation_prevnext"
+msgid "ctxcomponents_metadata_description"
+msgstr ""
+
+msgid "ctxcomponents_prevnext"
msgstr "previous / next entity"
-msgid "contentnavigation_prevnext_description"
+msgid "ctxcomponents_prevnext_description"
msgstr ""
"display link to go from one entity to another on entities implementing the "
"\"previous/next\" interface."
-msgid "contentnavigation_seealso"
+msgid "ctxcomponents_seealso"
msgstr "see also"
-msgid "contentnavigation_seealso_description"
+msgid "ctxcomponents_seealso_description"
msgstr ""
"section containing entities related by the \"see also\" relation on entities "
"supporting it."
-msgid "contentnavigation_wfhistory"
+msgid "ctxcomponents_wfhistory"
msgstr "workflow history"
-msgid "contentnavigation_wfhistory_description"
+msgid "ctxcomponents_wfhistory_description"
msgstr "show the workflow's history."
msgid "context"
--- a/i18n/es.po Wed Aug 25 10:01:11 2010 +0200
+++ b/i18n/es.po Wed Aug 25 10:29:07 2010 +0200
@@ -1161,53 +1161,53 @@
msgid "boxes"
msgstr "Cajas"
-msgid "boxes_bookmarks_box"
+msgid "ctxcomponents_bookmarks_box"
msgstr "Caja de Favoritos"
-msgid "boxes_bookmarks_box_description"
+msgid "ctxcomponents_bookmarks_box_description"
msgstr "Muestra y permite administrar los favoritos del usuario"
-msgid "boxes_download_box"
+msgid "ctxcomponents_download_box"
msgstr "Configuración de caja de descargas"
-msgid "boxes_download_box_description"
+msgid "ctxcomponents_download_box_description"
msgstr "Caja que contiene los elementos descargados"
-msgid "boxes_edit_box"
+msgid "ctxcomponents_edit_box"
msgstr "Caja de Acciones"
-msgid "boxes_edit_box_description"
+msgid "ctxcomponents_edit_box_description"
msgstr ""
"Muestra las acciones posibles a ejecutar para los datos seleccionados"
-msgid "boxes_filter_box"
+msgid "ctxcomponents_filter_box"
msgstr "Filtros"
-msgid "boxes_filter_box_description"
+msgid "ctxcomponents_filter_box_description"
msgstr "Muestra los filtros aplicables a una búsqueda realizada"
-msgid "boxes_possible_views_box"
+msgid "ctxcomponents_possible_views_box"
msgstr "Caja de Vistas Posibles"
-msgid "boxes_possible_views_box_description"
+msgid "ctxcomponents_possible_views_box_description"
msgstr "Muestra las vistas posibles a aplicar a los datos seleccionados"
-msgid "boxes_rss"
+msgid "ctxcomponents_rss"
msgstr "Ícono RSS"
-msgid "boxes_rss_description"
+msgid "ctxcomponents_rss_description"
msgstr "Muestra el ícono RSS para vistas RSS"
-msgid "boxes_search_box"
+msgid "ctxcomponents_search_box"
msgstr "Caja de búsqueda"
-msgid "boxes_search_box_description"
+msgid "ctxcomponents_search_box_description"
msgstr "Permite realizar una búsqueda simple para cualquier tipo de dato en la aplicación"
-msgid "boxes_startup_views_box"
+msgid "ctxcomponents_startup_views_box"
msgstr "Caja Vistas de inicio"
-msgid "boxes_startup_views_box_description"
+msgid "ctxcomponents_startup_views_box_description"
msgstr "Muestra las vistas de inicio de la aplicación"
msgid "bug report sent"
@@ -1490,38 +1490,38 @@
msgid "contentnavigation"
msgstr "Componentes contextuales"
-msgid "contentnavigation_breadcrumbs"
+msgid "ctxcomponents_breadcrumbs"
msgstr "Ruta de Navegación"
-msgid "contentnavigation_breadcrumbs_description"
+msgid "ctxcomponents_breadcrumbs_description"
msgstr "Muestra la ruta que permite localizar la página actual en el Sistema"
-msgid "contentnavigation_metadata"
+msgid "ctxcomponents_metadata"
msgstr "Metadatos de la Entidad"
-msgid "contentnavigation_metadata_description"
+msgid "ctxcomponents_metadata_description"
msgstr ""
-msgid "contentnavigation_prevnext"
+msgid "ctxcomponents_prevnext"
msgstr "Elemento anterior / siguiente"
-msgid "contentnavigation_prevnext_description"
+msgid "ctxcomponents_prevnext_description"
msgstr ""
"Muestra las ligas que permiten pasar de una entidad a otra en las entidades "
"que implementan la interface \"anterior/siguiente\"."
-msgid "contentnavigation_seealso"
+msgid "ctxcomponents_seealso"
msgstr "Vea también"
-msgid "contentnavigation_seealso_description"
+msgid "ctxcomponents_seealso_description"
msgstr ""
"sección que muestra las entidades relacionadas por la relación \"vea también\" , "
"si la entidad soporta esta relación."
-msgid "contentnavigation_wfhistory"
+msgid "ctxcomponents_wfhistory"
msgstr "Histórico del workflow."
-msgid "contentnavigation_wfhistory_description"
+msgid "ctxcomponents_wfhistory_description"
msgstr ""
"Sección que muestra el reporte histórico de las transiciones del workflow."
" Aplica solo en entidades con workflow."
--- a/i18n/fr.po Wed Aug 25 10:01:11 2010 +0200
+++ b/i18n/fr.po Wed Aug 25 10:29:07 2010 +0200
@@ -1164,53 +1164,53 @@
msgid "boxes"
msgstr "boîtes"
-msgid "boxes_bookmarks_box"
+msgid "ctxcomponents_bookmarks_box"
msgstr "boîte signets"
-msgid "boxes_bookmarks_box_description"
+msgid "ctxcomponents_bookmarks_box_description"
msgstr "boîte contenant les signets de l'utilisateur"
-msgid "boxes_download_box"
+msgid "ctxcomponents_download_box"
msgstr "boîte de téléchargement"
-msgid "boxes_download_box_description"
+msgid "ctxcomponents_download_box_description"
msgstr "boîte contenant un lien permettant de télécharger la ressource"
-msgid "boxes_edit_box"
+msgid "ctxcomponents_edit_box"
msgstr "boîte d'actions"
-msgid "boxes_edit_box_description"
+msgid "ctxcomponents_edit_box_description"
msgstr ""
"boîte affichant les différentes actions possibles sur les données affichées"
-msgid "boxes_filter_box"
+msgid "ctxcomponents_filter_box"
msgstr "filtrer"
-msgid "boxes_filter_box_description"
+msgid "ctxcomponents_filter_box_description"
msgstr "boîte permettant de filtrer parmi les résultats d'une recherche"
-msgid "boxes_possible_views_box"
+msgid "ctxcomponents_possible_views_box"
msgstr "boîte des vues possibles"
-msgid "boxes_possible_views_box_description"
+msgid "ctxcomponents_possible_views_box_description"
msgstr "boîte affichant les vues possibles pour les données courantes"
-msgid "boxes_rss"
+msgid "ctxcomponents_rss"
msgstr "icône RSS"
-msgid "boxes_rss_description"
+msgid "ctxcomponents_rss_description"
msgstr "l'icône RSS permettant de récupérer la vue RSS des données affichées"
-msgid "boxes_search_box"
+msgid "ctxcomponents_search_box"
msgstr "boîte de recherche"
-msgid "boxes_search_box_description"
+msgid "ctxcomponents_search_box_description"
msgstr "boîte avec un champ de recherche simple"
-msgid "boxes_startup_views_box"
+msgid "ctxcomponents_startup_views_box"
msgstr "boîte des vues de départs"
-msgid "boxes_startup_views_box_description"
+msgid "ctxcomponents_startup_views_box_description"
msgstr "boîte affichant les vues de départs de l'application"
msgid "bug report sent"
@@ -1491,42 +1491,42 @@
msgid "content type"
msgstr "type MIME"
-msgid "contentnavigation"
+msgid "ctxcomponents"
msgstr "composants contextuels"
-msgid "contentnavigation_breadcrumbs"
+msgid "ctxcomponents_breadcrumbs"
msgstr "fil d'ariane"
-msgid "contentnavigation_breadcrumbs_description"
+msgid "ctxcomponents_breadcrumbs_description"
msgstr ""
"affiche un chemin permettant de localiser la page courante dans le site"
-msgid "contentnavigation_metadata"
+msgid "ctxcomponents_metadata"
msgstr "méta-données de l'entité"
-msgid "contentnavigation_metadata_description"
+msgid "ctxcomponents_metadata_description"
msgstr ""
-msgid "contentnavigation_prevnext"
+msgid "ctxcomponents_prevnext"
msgstr "élément précedent / suivant"
-msgid "contentnavigation_prevnext_description"
+msgid "ctxcomponents_prevnext_description"
msgstr ""
"affiche des liens permettant de passer d'une entité à une autre sur les "
"entités implémentant l'interface \"précédent/suivant\"."
-msgid "contentnavigation_seealso"
+msgid "ctxcomponents_seealso"
msgstr "voir aussi"
-msgid "contentnavigation_seealso_description"
+msgid "ctxcomponents_seealso_description"
msgstr ""
"section affichant les entités liées par la relation \"voir aussi\" si "
"l'entité supporte cette relation."
-msgid "contentnavigation_wfhistory"
+msgid "ctxcomponents_wfhistory"
msgstr "historique du workflow."
-msgid "contentnavigation_wfhistory_description"
+msgid "ctxcomponents_wfhistory_description"
msgstr ""
"section affichant l'historique du workflow pour les entités ayant un "
"workflow."
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.10.0_Any.py Wed Aug 25 10:29:07 2010 +0200
@@ -0,0 +1,5 @@
+# rename cwprops for boxes/contentnavigation
+for x in rql('Any X,XK WHERE X pkey XK, '
+ 'X pkey ~= "boxes.%s" OR '
+ 'X pkey ~= "contentnavigation.%s"').entities():
+ x.set_attributes(pkey=u'ctxcomponents.' + x.pkey.split('.',1))
--- a/req.py Wed Aug 25 10:01:11 2010 +0200
+++ b/req.py Wed Aug 25 10:29:07 2010 +0200
@@ -15,9 +15,8 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-"""Base class for request/session
+"""Base class for request/session"""
-"""
__docformat__ = "restructuredtext en"
from warnings import warn
--- a/selectors.py Wed Aug 25 10:01:11 2010 +0200
+++ b/selectors.py Wed Aug 25 10:29:07 2010 +0200
@@ -1203,6 +1203,7 @@
# Web request selectors ########################################################
+# XXX deprecate
@objectify_selector
@lltrace
def primary_view(cls, req, view=None, **kwargs):
@@ -1252,6 +1253,7 @@
return 1
+# XXX deprecate
@objectify_selector
@lltrace
def match_context_prop(cls, req, context=None, **kwargs):
@@ -1272,8 +1274,6 @@
return 1
propval = req.property_value('%s.%s.context' % (cls.__registry__,
cls.__regid__))
- if not propval:
- propval = cls.context
if propval and context != propval:
return 0
return 1
--- a/utils.py Wed Aug 25 10:01:11 2010 +0200
+++ b/utils.py Wed Aug 25 10:29:07 2010 +0200
@@ -75,6 +75,31 @@
return False
return True
+
+class wrap_on_write(object):
+ def __init__(self, w, tag, closetag=None):
+ self.written = False
+ self.tag = unicode(tag)
+ self.closetag = closetag
+ self.w = w
+
+ def __enter__(self):
+ return self
+
+ def __call__(self, data):
+ if self.written is False:
+ self.w(self.tag)
+ self.written = True
+ self.w(data)
+
+ def __exit__(self, exctype, value, traceback):
+ if self.written is True:
+ if self.closetag:
+ self.w(unicode(self.closetag))
+ else:
+ self.w(self.tag.replace('<', '</', 1))
+
+
# use networkX instead ?
# http://networkx.lanl.gov/reference/algorithms.traversal.html#module-networkx.algorithms.traversal.astar
def transitive_closure_of(entity, relname, _seen=None):
--- a/web/box.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/box.py Wed Aug 25 10:29:07 2010 +0200
@@ -23,14 +23,10 @@
from logilab.mtconverter import xml_escape
from logilab.common.deprecation import class_deprecated, class_renamed
-from cubicweb import Unauthorized, role as get_role, target as get_target, tags
+from cubicweb import Unauthorized, role as get_role, target as get_target
from cubicweb.schema import display_name
-from cubicweb.selectors import (no_cnx, one_line_rset, primary_view,
- match_context_prop, partial_relation_possible,
- partial_has_related_entities)
-from cubicweb.appobject import AppObject
-from cubicweb.view import View, ReloadableMixIn, Component
-from cubicweb.uilib import domid, js
+from cubicweb.selectors import no_cnx, one_line_rset
+from cubicweb.view import View
from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs
from cubicweb.web.htmlwidgets import (BoxLink, BoxWidget, SideBoxWidget,
RawBoxItem, BoxSeparator)
@@ -55,324 +51,14 @@
return result
-class EditRelationMixIn(ReloadableMixIn):
- def box_item(self, entity, etarget, rql, label):
- """builds HTML link to edit relation between `entity` and `etarget`"""
- role, target = get_role(self), get_target(self)
- args = {role[0] : entity.eid, target[0] : etarget.eid}
- url = self._cw.user_rql_callback((rql, args))
- # for each target, provide a link to edit the relation
- return u'[<a href="%s">%s</a>] %s' % (xml_escape(url), label,
- etarget.view('incontext'))
-
- def related_boxitems(self, entity):
- rql = 'DELETE S %s O WHERE S eid %%(s)s, O eid %%(o)s' % self.rtype
- return [self.box_item(entity, etarget, rql, u'-')
- for etarget in self.related_entities(entity)]
-
- def related_entities(self, entity):
- return entity.related(self.rtype, get_role(self), entities=True)
-
- def unrelated_boxitems(self, entity):
- rql = 'SET S %s O WHERE S eid %%(s)s, O eid %%(o)s' % self.rtype
- return [self.box_item(entity, etarget, rql, u'+')
- for etarget in self.unrelated_entities(entity)]
-
- def unrelated_entities(self, entity):
- """returns the list of unrelated entities, using the entity's
- appropriate vocabulary function
- """
- skip = set(unicode(e.eid) for e in entity.related(self.rtype, get_role(self),
- entities=True))
- skip.add(None)
- skip.add(INTERNAL_FIELD_VALUE)
- filteretype = getattr(self, 'etype', None)
- entities = []
- form = self._cw.vreg['forms'].select('edition', self._cw,
- rset=self.cw_rset,
- row=self.cw_row or 0)
- field = form.field_by_name(self.rtype, get_role(self), entity.e_schema)
- for _, eid in field.vocabulary(form):
- if eid not in skip:
- entity = self._cw.entity_from_eid(eid)
- if filteretype is None or entity.__regid__ == filteretype:
- entities.append(entity)
- return entities
-
-
-# generic classes for the new box system #######################################
-
-from cubicweb.selectors import match_context, contextual
-
-class EmptyComponent(Exception):
- """some selectable component has actually no content and should not be
- rendered
- """
-
-class Layout(Component):
- __regid__ = 'layout'
- __abstract__ = True
-
-
-class Box(AppObject): # XXX ContextComponent
- __registry__ = 'boxes'
- __select__ = ~no_cnx() & match_context_prop()
-
- categories_in_order = ()
- cw_property_defs = {
- _('visible'): dict(type='Boolean', default=True,
- help=_('display the box or not')),
- _('order'): dict(type='Int', default=99,
- help=_('display order of the box')),
- # XXX 'incontext' boxes are handled by the default primary view
- _('context'): dict(type='String', default='left',
- vocabulary=(_('left'), _('incontext'), _('right')),
- help=_('context where this box should be displayed')),
- }
- context = 'left'
- contextual = False
- title = None
- # XXX support kwargs for compat with old boxes which gets the view as
- # argument
- def render(self, w, **kwargs):
- getlayout = self._cw.vreg['components'].select
- try:
- # XXX ensure context is given when the component is reloaded through
- # ajax
- context = self.cw_extra_kwargs['context']
- except KeyError:
- context = self.cw_propval('context')
- layout = getlayout('layout', self._cw, rset=self.cw_rset,
- row=self.cw_row, col=self.cw_col,
- view=self, context=context)
- layout.render(w)
-
- def init_rendering(self):
- """init rendering callback: that's the good time to check your component
- has some content to display. If not, you can still raise
- :exc:`EmptyComponent` to inform it should be skipped.
-
- Also, :exc:`Unauthorized` will be catched, logged, then the component
- will be skipped.
- """
- self.items = []
-
- @property
- def domid(self):
- """return the HTML DOM identifier for this component"""
- return domid(self.__regid__)
-
- @property
- def cssclass(self):
- """return the CSS class name for this component"""
- return domid(self.__regid__)
-
- def render_title(self, w):
- """return the title for this component"""
- if self.title is None:
- raise NotImplementedError()
- w(self._cw._(self.title))
-
- def render_body(self, w):
- """return the body (content) for this component"""
- raise NotImplementedError()
-
- def render_items(self, w, items=None, klass=u'boxListing'):
- if items is None:
- items = self.items
- assert items
- w(u'<ul class="%s">' % klass)
- for item in items:
- if hasattr(item, 'render'):
- item.render(w) # XXX display <li> by itself
- else:
- w(u'<li>')
- w(item)
- w(u'</li>')
- w(u'</ul>')
-
- def append(self, item):
- self.items.append(item)
-
- def box_action(self, action): # XXX action_link
- return self.build_link(self._cw._(action.title), action.url())
-
- def build_link(self, title, url, **kwargs):
- if self._cw.selected(url):
- try:
- kwargs['klass'] += ' selected'
- except KeyError:
- kwargs['klass'] = 'selected'
- return tags.a(title, href=url, **kwargs)
-
-
-class EntityBox(Box): # XXX ContextEntityComponent
- """base class for boxes related to a single entity"""
- __select__ = Box.__select__ & one_line_rset()
- context = 'incontext'
- contextual = True
-
- def __init__(self, *args, **kwargs):
- super(EntityBox, self).__init__(*args, **kwargs)
- try:
- entity = kwargs['entity']
- except KeyError:
- entity = self.cw_rset.get_entity(self.cw_row or 0, self.cw_col or 0)
- self.entity = entity
-
- @property
- def domid(self):
- return domid(self.__regid__) + unicode(self.entity.eid)
-
-
-# high level abstract box classes ##############################################
-
-
-class RQLBox(Box):
- """abstract box for boxes displaying the content of a rql query not
- related to the current result set.
- """
- rql = None
-
- def to_display_rql(self):
- assert self.rql is not None, self.__regid__
- return (self.rql,)
-
- def init_rendering(self):
- rset = self._cw.execute(*self.to_display_rql())
- if not rset:
- raise EmptyComponent()
- if len(rset[0]) == 2:
- self.items = []
- for i, (eid, label) in enumerate(rset):
- entity = rset.get_entity(i, 0)
- self.items.append(self.build_link(label, entity.absolute_url()))
- else:
- self.items = [self.build_link(e.dc_title(), e.absolute_url())
- for e in rset.entities()]
-
- def render_body(self, w):
- self.render_items(w)
-
-
-class EditRelationBox(EditRelationMixIn, EntityBox):
- """base class for boxes which let add or remove entities linked by a given
- relation
-
- subclasses should define at least id, rtype and target class attributes.
- """
- def render_title(self, w):
- return display_name(self._cw, self.rtype, get_role(self),
- context=self.entity.__regid__)
-
- def render_body(self, w):
- self._cw.add_js('cubicweb.ajax.js')
- related = self.related_boxitems(self.entity)
- unrelated = self.unrelated_boxitems(self.entity)
- self.items.extend(related)
- if related and unrelated:
- self.items.append(BoxSeparator())
- self.items.extend(unrelated)
- self.render_items(w)
-
-
-class AjaxEditRelationBox(EntityBox):
- __select__ = EntityBox.__select__ & (
- partial_relation_possible(action='add') | partial_has_related_entities())
-
- # view used to display related entties
- item_vid = 'incontext'
- # values separator when multiple values are allowed
- separator = ','
- # msgid of the message to display when some new relation has been added/removed
- added_msg = None
- removed_msg = None
-
- # class attributes below *must* be set in concret classes (additionaly to
- # rtype / role [/ target_etype]. They should correspond to js_* methods on
- # the json controller
-
- # function(eid)
- # -> expected to return a list of values to display as input selector
- # vocabulary
- fname_vocabulary = None
-
- # function(eid, value)
- # -> handle the selector's input (eg create necessary entities and/or
- # relations). If the relation is multiple, you'll get a list of value, else
- # a single string value.
- fname_validate = None
-
- # function(eid, linked entity eid)
- # -> remove the relation
- fname_remove = None
-
- def __init__(self, *args, **kwargs):
- super(AjaxEditRelationBox, self).__init__(*args, **kwargs)
- self.rdef = self.entity.e_schema.rdef(self.rtype, self.role, self.target_etype)
-
- def render_title(self, w):
- w(self.rdef.rtype.display_name(self._cw, self.role,
- context=self.entity.__regid__))
-
- def render_body(self, w):
- req = self._cw
- entity = self.entity
- related = entity.related(self.rtype, self.role)
- if self.role == 'subject':
- mayadd = self.rdef.has_perm(req, 'add', fromeid=entity.eid)
- maydel = self.rdef.has_perm(req, 'delete', fromeid=entity.eid)
- else:
- mayadd = self.rdef.has_perm(req, 'add', toeid=entity.eid)
- maydel = self.rdef.has_perm(req, 'delete', toeid=entity.eid)
- if mayadd or maydel:
- req.add_js(('cubicweb.ajax.js', 'cubicweb.ajax.box.js'))
- _ = req._
- if related:
- w(u'<table>')
- for rentity in related.entities():
- # for each related entity, provide a link to remove the relation
- subview = rentity.view(self.item_vid)
- if maydel:
- jscall = unicode(js.ajaxBoxRemoveLinkedEntity(
- self.__regid__, entity.eid, rentity.eid,
- self.fname_remove,
- self.removed_msg and _(self.removed_msg)))
- w(u'<tr><td>[<a href="javascript: %s">-</a>]</td>'
- '<td class="tagged"> %s</td></tr>' % (xml_escape(jscall),
- subview))
- else:
- w(u'<tr><td class="tagged">%s</td></tr>' % (subview))
- w(u'</table>')
- else:
- w(_('no related entity'))
- if mayadd:
- req.add_js('jquery.autocomplete.js')
- req.add_css('jquery.autocomplete.css')
- multiple = self.rdef.role_cardinality(self.role) in '*+'
- w(u'<table><tr><td>')
- jscall = unicode(js.ajaxBoxShowSelector(
- self.__regid__, entity.eid, self.fname_vocabulary,
- self.fname_validate, self.added_msg and _(self.added_msg),
- _(stdmsgs.BUTTON_OK[0]), _(stdmsgs.BUTTON_CANCEL[0]),
- multiple and self.separator))
- w('<a class="button sglink" href="javascript: %s">%s</a>' % (
- xml_escape(jscall),
- multiple and _('add_relation') or _('update_relation')))
- w(u'</td><td>')
- w(u'<div id="%sHolder"></div>' % self.domid)
- w(u'</td></tr></table>')
-
-
# old box system, deprecated ###################################################
class BoxTemplate(View):
"""base template for boxes, usually a (contextual) list of possible
-
actions. Various classes attributes may be used to control the box
rendering.
- You may override on of the formatting callbacks is this is not necessary
+ You may override one of the formatting callbacks if this is not necessary
for your custom box.
Classes inheriting from this class usually only have to override call
@@ -381,10 +67,10 @@
box.render(self.w)
"""
__metaclass__ = class_deprecated
- __deprecation_warning__ = '*BoxTemplate classes are deprecated, use *Box instead'
+ __deprecation_warning__ = '[3.10] *BoxTemplate classes are deprecated, use *CtxComponent instead (%(cls)s)'
- __registry__ = 'boxes'
- __select__ = ~no_cnx() & match_context_prop()
+ __registry__ = 'ctxcomponents'
+ __select__ = ~no_cnx()
categories_in_order = ()
cw_property_defs = {
@@ -465,13 +151,15 @@
class EntityBoxTemplate(BoxTemplate):
"""base class for boxes related to a single entity"""
- __select__ = BoxTemplate.__select__ & one_line_rset() & primary_view()
+ __select__ = BoxTemplate.__select__ & one_line_rset()
context = 'incontext'
def call(self, row=0, col=0, **kwargs):
"""classes inheriting from EntityBoxTemplate should define cell_call"""
self.cell_call(row, col, **kwargs)
+from cubicweb.web.component import AjaxEditRelationCtxComponent, EditRelationMixIn
+
class EditRelationBoxTemplate(EditRelationMixIn, EntityBoxTemplate):
"""base class for boxes which let add or remove entities linked
@@ -502,6 +190,6 @@
AjaxEditRelationBoxTemplate = class_renamed(
- 'AjaxEditRelationBoxTemplate', AjaxEditRelationBox,
- '[3.10] AjaxEditRelationBoxTemplate has been renamed to AjaxEditRelationBox')
+ 'AjaxEditRelationBoxTemplate', AjaxEditRelationCtxComponent,
+ '[3.10] AjaxEditRelationBoxTemplate has been renamed to AjaxEditRelationCtxComponent')
--- a/web/component.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/component.py Wed Aug 25 10:29:07 2010 +0200
@@ -22,57 +22,20 @@
__docformat__ = "restructuredtext en"
_ = unicode
-from logilab.common.deprecation import class_renamed
+from logilab.common.deprecation import class_deprecated, class_renamed
from logilab.mtconverter import xml_escape
-from cubicweb import role
-from cubicweb.utils import json_dumps
-from cubicweb.view import Component
-from cubicweb.selectors import (
- paginated_rset, one_line_rset, primary_view, match_context_prop,
- partial_has_related_entities)
+from cubicweb import Unauthorized, role, tags
+from cubicweb.uilib import js, domid
+from cubicweb.view import ReloadableMixIn, Component
+from cubicweb.selectors 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 htmlwidgets, stdmsgs
-class EntityVComponent(Component):
- """abstract base class for additinal components displayed in content
- headers and footer according to:
-
- * the displayed entity's type
- * a context (currently 'header' or 'footer')
-
- it should be configured using .accepts, .etype, .rtype, .target and
- .context class attributes
- """
-
- __registry__ = 'contentnavigation'
- __select__ = one_line_rset() & primary_view() & match_context_prop()
-
- cw_property_defs = {
- _('visible'): dict(type='Boolean', default=True,
- help=_('display the component or not')),
- _('order'): dict(type='Int', default=99,
- help=_('display order of the component')),
- _('context'): dict(type='String', default='navtop',
- vocabulary=(_('navtop'), _('navbottom'),
- _('navcontenttop'), _('navcontentbottom'),
- _('ctxtoolbar')),
- help=_('context where this component should be displayed')),
- }
-
- context = 'navcontentbottom'
-
- def call(self, view=None):
- if self.cw_rset is None:
- self.entity_call(self.cw_extra_kwargs.pop('entity'))
- else:
- self.cell_call(0, 0, view=view)
-
- def cell_call(self, row, col, view=None):
- self.entity_call(self.cw_rset.get_entity(row, col), view=view)
-
- def entity_call(self, entity, view=None):
- raise NotImplementedError()
-
+# abstract base class for navigation components ################################
class NavigationComponent(Component):
"""abstract base class for navigation components"""
@@ -145,10 +108,9 @@
elif path == 'json':
rql = params.pop('rql', self.cw_rset.printable_rql())
# latest 'true' used for 'swap' mode
- url = 'javascript: replacePageChunk(%s, %s, %s, %s, true)' % (
- json_dumps(params.get('divid', 'pageContent')),
- json_dumps(rql), json_dumps(params.pop('vid', None)),
- json_dumps(params))
+ url = 'javascript: %s' % (js.replacePageChunk(
+ params.get('divid', 'pageContent'), rql,
+ params.pop('vid', None), params))
else:
url = self._cw.build_url(path, **params)
return url
@@ -177,6 +139,405 @@
return self.next_page_link_templ % (url, 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 Layout(Component):
+ __regid__ = 'layout'
+ __abstract__ = True
+
+ def init_rendering(self):
+ """init view for rendering. Return true if we should go on, false
+ if we should stop now.
+ """
+ view = self.cw_extra_kwargs['view']
+ try:
+ view.init_rendering()
+ except Unauthorized, ex:
+ self.warning("can't render %s: %s", view, ex)
+ return False
+ except EmptyComponent:
+ return False
+ return True
+
+
+class CtxComponent(AppObject):
+ """base class for contextual compontents. The following contexts are
+ predefined:
+
+ * boxes: 'left', 'incontext', 'right'
+ * section: 'navcontenttop', 'navcontentbottom', 'navtop', 'navbottom'
+ * other: 'ctxtoolbar'
+
+ The 'incontext', 'navcontenttop', 'navcontentbottom' and 'ctxtoolbar'
+ context are handled by the default primary view, others by the default main
+ template.
+
+ All subclasses may not support all those contexts (for instance if it can't
+ be displayed as box, or as a toolbar icon). You may restrict allowed context
+ as followed:
+
+ .. sourcecode:: python
+
+ class MyComponent(CtxComponent):
+ cw_property_defs = override_ctx(CtxComponent,
+ vocabulary=[list of contexts])
+ context = 'my default context'
+
+ You can configure default component's context by simply giving appropriate
+ value to the `context` class attribute, as seen above.
+ """
+ __registry__ = 'ctxcomponents'
+ __select__ = ~no_cnx()
+
+ categories_in_order = ()
+ cw_property_defs = {
+ _('visible'): dict(type='Boolean', default=True,
+ help=_('display the box or not')),
+ _('order'): dict(type='Int', default=99,
+ help=_('display order of the box')),
+ _('context'): dict(type='String', default='left',
+ vocabulary=(_('left'), _('incontext'), _('right'),
+ _('navtop'), _('navbottom'),
+ _('navcontenttop'), _('navcontentbottom'),
+ _('ctxtoolbar')),
+ help=_('context where this component should be displayed')),
+ }
+ context = 'left'
+ contextual = False
+ title = None
+
+ # XXX support kwargs for compat with old boxes which gets the view as
+ # argument
+ def render(self, w, **kwargs):
+ getlayout = self._cw.vreg['components'].select
+ try:
+ # XXX ensure context is given when the component is reloaded through
+ # ajax
+ context = self.cw_extra_kwargs['context']
+ except KeyError:
+ context = self.cw_propval('context')
+ layout = getlayout('layout', self._cw, rset=self.cw_rset,
+ row=self.cw_row, col=self.cw_col,
+ view=self, context=context)
+ layout.render(w)
+
+ def init_rendering(self):
+ """init rendering callback: that's the good time to check your component
+ has some content to display. If not, you can still raise
+ :exc:`EmptyComponent` to inform it should be skipped.
+
+ Also, :exc:`Unauthorized` will be catched, logged, then the component
+ will be skipped.
+ """
+ self.items = []
+
+ @property
+ def domid(self):
+ """return the HTML DOM identifier for this component"""
+ return domid(self.__regid__)
+
+ @property
+ def cssclass(self):
+ """return the CSS class name for this component"""
+ return domid(self.__regid__)
+
+ def render_title(self, w):
+ """return the title for this component"""
+ if self.title:
+ w(self._cw._(self.title))
+
+ def render_body(self, w):
+ """return the body (content) for this component"""
+ raise NotImplementedError()
+
+ def render_items(self, w, items=None, klass=u'boxListing'):
+ if items is None:
+ items = self.items
+ assert items
+ w(u'<ul class="%s">' % klass)
+ for item in items:
+ if hasattr(item, 'render'):
+ item.render(w) # XXX display <li> by itself
+ else:
+ w(u'<li>')
+ w(item)
+ w(u'</li>')
+ w(u'</ul>')
+
+ def append(self, item):
+ self.items.append(item)
+
+ def box_action(self, action): # XXX action_link
+ return self.build_link(self._cw._(action.title), action.url())
+
+ def build_link(self, title, url, **kwargs):
+ if self._cw.selected(url):
+ try:
+ kwargs['klass'] += ' selected'
+ except KeyError:
+ kwargs['klass'] = 'selected'
+ return tags.a(title, href=url, **kwargs)
+
+
+class EntityCtxComponent(CtxComponent):
+ """base class for boxes related to a single entity"""
+ __select__ = CtxComponent.__select__ & non_final_entity() & one_line_rset()
+ context = 'incontext'
+ contextual = True
+
+ def __init__(self, *args, **kwargs):
+ super(EntityCtxComponent, self).__init__(*args, **kwargs)
+ try:
+ entity = kwargs['entity']
+ except KeyError:
+ entity = self.cw_rset.get_entity(self.cw_row or 0, self.cw_col or 0)
+ self.entity = entity
+
+ @property
+ def domid(self):
+ return domid(self.__regid__) + unicode(self.entity.eid)
+
+
+# high level abstract classes ##################################################
+
+class RQLCtxComponent(CtxComponent):
+ """abstract box for boxes displaying the content of a rql query not
+ related to the current result set.
+ """
+ rql = None
+
+ def to_display_rql(self):
+ assert self.rql is not None, self.__regid__
+ return (self.rql,)
+
+ def init_rendering(self):
+ rset = self._cw.execute(*self.to_display_rql())
+ if not rset:
+ raise EmptyComponent()
+ if len(rset[0]) == 2:
+ self.items = []
+ for i, (eid, label) in enumerate(rset):
+ entity = rset.get_entity(i, 0)
+ self.items.append(self.build_link(label, entity.absolute_url()))
+ else:
+ self.items = [self.build_link(e.dc_title(), e.absolute_url())
+ for e in rset.entities()]
+
+ def render_body(self, w):
+ self.render_items(w)
+
+
+class EditRelationMixIn(ReloadableMixIn):
+ def box_item(self, entity, etarget, rql, label):
+ """builds HTML link to edit relation between `entity` and `etarget`"""
+ role, target = get_role(self), get_target(self)
+ args = {role[0] : entity.eid, target[0] : etarget.eid}
+ url = self._cw.user_rql_callback((rql, args))
+ # for each target, provide a link to edit the relation
+ return u'[<a href="%s">%s</a>] %s' % (xml_escape(url), label,
+ etarget.view('incontext'))
+
+ def related_boxitems(self, entity):
+ rql = 'DELETE S %s O WHERE S eid %%(s)s, O eid %%(o)s' % self.rtype
+ return [self.box_item(entity, etarget, rql, u'-')
+ for etarget in self.related_entities(entity)]
+
+ def related_entities(self, entity):
+ return entity.related(self.rtype, get_role(self), entities=True)
+
+ def unrelated_boxitems(self, entity):
+ rql = 'SET S %s O WHERE S eid %%(s)s, O eid %%(o)s' % self.rtype
+ return [self.box_item(entity, etarget, rql, u'+')
+ for etarget in self.unrelated_entities(entity)]
+
+ def unrelated_entities(self, entity):
+ """returns the list of unrelated entities, using the entity's
+ appropriate vocabulary function
+ """
+ skip = set(unicode(e.eid) for e in entity.related(self.rtype, get_role(self),
+ entities=True))
+ skip.add(None)
+ skip.add(INTERNAL_FIELD_VALUE)
+ filteretype = getattr(self, 'etype', None)
+ entities = []
+ form = self._cw.vreg['forms'].select('edition', self._cw,
+ rset=self.cw_rset,
+ row=self.cw_row or 0)
+ field = form.field_by_name(self.rtype, get_role(self), entity.e_schema)
+ for _, eid in field.vocabulary(form):
+ if eid not in skip:
+ entity = self._cw.entity_from_eid(eid)
+ if filteretype is None or entity.__regid__ == filteretype:
+ entities.append(entity)
+ return entities
+
+
+class EditRelationCtxComponent(EditRelationMixIn, EntityCtxComponent):
+ """base class for boxes which let add or remove entities linked by a given
+ relation
+
+ subclasses should define at least id, rtype and target class attributes.
+ """
+ def render_title(self, w):
+ return display_name(self._cw, self.rtype, get_role(self),
+ context=self.entity.__regid__)
+
+ def render_body(self, w):
+ self._cw.add_js('cubicweb.ajax.js')
+ related = self.related_boxitems(self.entity)
+ unrelated = self.unrelated_boxitems(self.entity)
+ self.items.extend(related)
+ if related and unrelated:
+ self.items.append(htmlwidgets.BoxSeparator())
+ self.items.extend(unrelated)
+ self.render_items(w)
+
+
+class AjaxEditRelationCtxComponent(EntityCtxComponent):
+ __select__ = EntityCtxComponent.__select__ & (
+ partial_relation_possible(action='add') | partial_has_related_entities())
+
+ # view used to display related entties
+ item_vid = 'incontext'
+ # values separator when multiple values are allowed
+ separator = ','
+ # msgid of the message to display when some new relation has been added/removed
+ added_msg = None
+ removed_msg = None
+
+ # class attributes below *must* be set in concret classes (additionaly to
+ # rtype / role [/ target_etype]. They should correspond to js_* methods on
+ # the json controller
+
+ # function(eid)
+ # -> expected to return a list of values to display as input selector
+ # vocabulary
+ fname_vocabulary = None
+
+ # function(eid, value)
+ # -> handle the selector's input (eg create necessary entities and/or
+ # relations). If the relation is multiple, you'll get a list of value, else
+ # a single string value.
+ fname_validate = None
+
+ # function(eid, linked entity eid)
+ # -> remove the relation
+ fname_remove = None
+
+ def __init__(self, *args, **kwargs):
+ super(AjaxEditRelationCtxComponent, self).__init__(*args, **kwargs)
+ self.rdef = self.entity.e_schema.rdef(self.rtype, self.role, self.target_etype)
+
+ def render_title(self, w):
+ w(self.rdef.rtype.display_name(self._cw, self.role,
+ context=self.entity.__regid__))
+
+ def render_body(self, w):
+ req = self._cw
+ entity = self.entity
+ related = entity.related(self.rtype, self.role)
+ if self.role == 'subject':
+ mayadd = self.rdef.has_perm(req, 'add', fromeid=entity.eid)
+ maydel = self.rdef.has_perm(req, 'delete', fromeid=entity.eid)
+ else:
+ mayadd = self.rdef.has_perm(req, 'add', toeid=entity.eid)
+ maydel = self.rdef.has_perm(req, 'delete', toeid=entity.eid)
+ if mayadd or maydel:
+ req.add_js(('cubicweb.ajax.js', 'cubicweb.ajax.box.js'))
+ _ = req._
+ if related:
+ w(u'<table>')
+ for rentity in related.entities():
+ # for each related entity, provide a link to remove the relation
+ subview = rentity.view(self.item_vid)
+ if maydel:
+ jscall = unicode(js.ajaxBoxRemoveLinkedEntity(
+ self.__regid__, entity.eid, rentity.eid,
+ self.fname_remove,
+ self.removed_msg and _(self.removed_msg)))
+ w(u'<tr><td>[<a href="javascript: %s">-</a>]</td>'
+ '<td class="tagged"> %s</td></tr>' % (xml_escape(jscall),
+ subview))
+ else:
+ w(u'<tr><td class="tagged">%s</td></tr>' % (subview))
+ w(u'</table>')
+ else:
+ w(_('no related entity'))
+ if mayadd:
+ req.add_js('jquery.autocomplete.js')
+ req.add_css('jquery.autocomplete.css')
+ multiple = self.rdef.role_cardinality(self.role) in '*+'
+ w(u'<table><tr><td>')
+ jscall = unicode(js.ajaxBoxShowSelector(
+ self.__regid__, entity.eid, self.fname_vocabulary,
+ self.fname_validate, self.added_msg and _(self.added_msg),
+ _(stdmsgs.BUTTON_OK[0]), _(stdmsgs.BUTTON_CANCEL[0]),
+ multiple and self.separator))
+ w('<a class="button sglink" href="javascript: %s">%s</a>' % (
+ xml_escape(jscall),
+ multiple and _('add_relation') or _('update_relation')))
+ w(u'</td><td>')
+ w(u'<div id="%sHolder"></div>' % self.domid)
+ w(u'</td></tr></table>')
+
+
+# old contextual components, deprecated ########################################
+
+class EntityVComponent(Component):
+ """abstract base class for additinal components displayed in content
+ headers and footer according to:
+
+ * the displayed entity's type
+ * a context (currently 'header' or 'footer')
+
+ it should be configured using .accepts, .etype, .rtype, .target and
+ .context class attributes
+ """
+ __metaclass__ = class_deprecated
+ __deprecation_warning__ = '[3.10] *VComponent classes are deprecated, use *CtxComponent instead (%(cls)s)'
+
+ __registry__ = 'ctxcomponents'
+ __select__ = one_line_rset()
+
+ cw_property_defs = {
+ _('visible'): dict(type='Boolean', default=True,
+ help=_('display the component or not')),
+ _('order'): dict(type='Int', default=99,
+ help=_('display order of the component')),
+ _('context'): dict(type='String', default='navtop',
+ vocabulary=(_('navtop'), _('navbottom'),
+ _('navcontenttop'), _('navcontentbottom'),
+ _('ctxtoolbar')),
+ help=_('context where this component should be displayed')),
+ }
+
+ context = 'navcontentbottom'
+
+ def call(self, view=None):
+ if self.cw_rset is None:
+ self.entity_call(self.cw_extra_kwargs.pop('entity'))
+ else:
+ self.cell_call(0, 0, view=view)
+
+ def cell_call(self, row, col, view=None):
+ self.entity_call(self.cw_rset.get_entity(row, col), view=view)
+
+ def entity_call(self, entity, view=None):
+ raise NotImplementedError()
+
+
class RelatedObjectsVComponent(EntityVComponent):
"""a section to display some related entities"""
__select__ = EntityVComponent.__select__ & partial_has_related_entities()
@@ -203,8 +564,9 @@
self.w(u'</div>')
+
VComponent = class_renamed('VComponent', Component,
- 'VComponent is deprecated, use Component')
+ '[3.2] VComponent is deprecated, use Component')
SingletonVComponent = class_renamed('SingletonVComponent', Component,
- 'SingletonVComponent is deprecated, use '
+ '[3.2] SingletonVComponent is deprecated, use '
'Component and explicit registration control')
--- a/web/test/unittest_views_navigation.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/test/unittest_views_navigation.py Wed Aug 25 10:29:07 2010 +0200
@@ -122,26 +122,26 @@
# view = mock_object(is_primary=lambda x: True)
# rset = self.execute('CWUser X LIMIT 1')
# req = self.request()
- # objs = self.vreg['contentnavigation'].poss_visible_objects(
+ # objs = self.vreg['ctxcomponents'].poss_visible_objects(
# req, rset=rset, view=view, context='navtop')
# # breadcrumbs should be in headers by default
# clsids = set(obj.id for obj in objs)
# self.failUnless('breadcrumbs' in clsids)
- # objs = self.vreg['contentnavigation'].poss_visible_objects(
+ # objs = self.vreg['ctxcomponents'].poss_visible_objects(
# req, rset=rset, view=view, context='navbottom')
# # breadcrumbs should _NOT_ be in footers by default
# clsids = set(obj.id for obj in objs)
# self.failIf('breadcrumbs' in clsids)
- # self.execute('INSERT CWProperty P: P pkey "contentnavigation.breadcrumbs.context", '
+ # self.execute('INSERT CWProperty P: P pkey "ctxcomponents.breadcrumbs.context", '
# 'P value "navbottom"')
# # breadcrumbs should now be in footers
# req.cnx.commit()
- # objs = self.vreg['contentnavigation'].poss_visible_objects(
+ # objs = self.vreg['ctxcomponents'].poss_visible_objects(
# req, rset=rset, view=view, context='navbottom')
# clsids = [obj.id for obj in objs]
# self.failUnless('breadcrumbs' in clsids)
- # objs = self.vreg['contentnavigation'].poss_visible_objects(
+ # objs = self.vreg['ctxcomponents'].poss_visible_objects(
# req, rset=rset, view=view, context='navtop')
# clsids = [obj.id for obj in objs]
--- a/web/test/unittest_viewselector.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/test/unittest_viewselector.py Wed Aug 25 10:29:07 2010 +0200
@@ -468,18 +468,18 @@
def test_properties(self):
self.assertEquals(sorted(k for k in self.vreg['propertydefs'].keys()
- if k.startswith('boxes.edit_box')),
- ['boxes.edit_box.context',
- 'boxes.edit_box.order',
- 'boxes.edit_box.visible'])
+ if k.startswith('ctxcomponents.edit_box')),
+ ['ctxcomponents.edit_box.context',
+ 'ctxcomponents.edit_box.order',
+ 'ctxcomponents.edit_box.visible'])
self.assertEquals([k for k in self.vreg['propertyvalues'].keys()
if not k.startswith('system.version')],
[])
- self.assertEquals(self.vreg.property_value('boxes.edit_box.visible'), True)
- self.assertEquals(self.vreg.property_value('boxes.edit_box.order'), 2)
- self.assertEquals(self.vreg.property_value('boxes.possible_views_box.visible'), False)
- self.assertEquals(self.vreg.property_value('boxes.possible_views_box.order'), 10)
- self.assertRaises(UnknownProperty, self.vreg.property_value, 'boxes.actions_box')
+ self.assertEquals(self.vreg.property_value('ctxcomponents.edit_box.visible'), True)
+ self.assertEquals(self.vreg.property_value('ctxcomponents.edit_box.order'), 2)
+ self.assertEquals(self.vreg.property_value('ctxcomponents.possible_views_box.visible'), False)
+ self.assertEquals(self.vreg.property_value('ctxcomponents.possible_views_box.order'), 10)
+ self.assertRaises(UnknownProperty, self.vreg.property_value, 'ctxcomponents.actions_box')
--- a/web/views/ajaxedit.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/ajaxedit.py Wed Aug 25 10:29:07 2010 +0200
@@ -22,7 +22,7 @@
from cubicweb import role
from cubicweb.view import View
from cubicweb.selectors import match_form_params, match_kwargs
-from cubicweb.web.box import EditRelationMixIn, EditRelationBoxTemplate
+from cubicweb.web.component import EditRelationMixIn
class AddRelationView(EditRelationMixIn, View):
"""base class for view which let add entities linked by a given relation
--- a/web/views/basecomponents.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/basecomponents.py Wed Aug 25 10:29:07 2010 +0200
@@ -20,6 +20,7 @@
* the rql input form
* the logged user link
"""
+from __future__ import with_statement
__docformat__ = "restructuredtext en"
_ = unicode
@@ -27,9 +28,11 @@
from logilab.mtconverter import xml_escape
from rql import parse
-from cubicweb.selectors import (yes, multi_etypes_rset, match_form_params,
+from cubicweb.selectors import (yes, multi_etypes_rset,
+ match_form_params, match_context,
anonymous_user, authenticated_user)
from cubicweb.schema import display_name
+from cubicweb.utils import wrap_on_write
from cubicweb.uilib import toggle_action
from cubicweb.web import component
from cubicweb.web.htmlwidgets import (MenuWidget, PopupBoxMenu, BoxSeparator,
@@ -166,18 +169,6 @@
self._cw.base_url(), xml_escape(title)))
-class SeeAlsoVComponent(component.RelatedObjectsVComponent):
- """display any entity's see also"""
- __regid__ = 'seealso'
- context = 'navcontentbottom'
- rtype = 'see_also'
- role = 'subject'
- order = 40
- # register msg not generated since no entity use see_also in cubicweb itself
- title = _('contentnavigation_seealso')
- help = _('contentnavigation_seealso_description')
-
-
class EtypeRestrictionComponent(component.Component):
"""displays the list of entity types contained in the resultset
to be able to filter accordingly.
@@ -229,17 +220,46 @@
self.w(u' | '.join(html))
self.w(u'</div>')
+# contextual components ########################################################
-class MetaDataComponent(component.EntityVComponent):
+# class SeeAlsoVComponent(component.RelatedObjectsVComponent):
+# """display any entity's see also"""
+# __regid__ = 'seealso'
+# context = 'navcontentbottom'
+# rtype = 'see_also'
+# role = 'subject'
+# order = 40
+# # register msg not generated since no entity use see_also in cubicweb itself
+# title = _('ctxcomponents_seealso')
+# help = _('ctxcomponents_seealso_description')
+
+
+class MetaDataComponent(component.EntityCtxComponent):
__regid__ = 'metadata'
context = 'navbottom'
order = 1
- def cell_call(self, row, col, view=None):
- self.wview('metadata', self.cw_rset, row=row, col=col)
+ def render_body(self, w):
+ self.entity.view('metadata', w=w)
-def registration_callback(vreg):
- vreg.register_all(globals().values(), __name__, (SeeAlsoVComponent,))
- if 'see_also' in vreg.schema:
- vreg.register(SeeAlsoVComponent)
+class SectionLayout(component.Layout):
+ __select__ = match_context('navtop', 'navbottom',
+ 'navcontenttop', 'navcontentbottom')
+ cssclass = 'section'
+
+ def render(self, w):
+ if self.init_rendering():
+ view = self.cw_extra_kwargs['view']
+ w(u'<div class="%s %s" id="%s">' % (self.cssclass, view.cssclass,
+ view.domid))
+ with wrap_on_write(w, '<h4>') as wow:
+ view.render_title(wow)
+ view.render_body(w)
+ w(u'</div>\n')
+
+
+# def registration_callback(vreg):
+# vreg.register_all(globals().values(), __name__, (SeeAlsoVComponent,))
+# if 'see_also' in vreg.schema:
+# vreg.register(SeeAlsoVComponent)
--- a/web/views/basetemplates.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/basetemplates.py Wed Aug 25 10:29:07 2010 +0200
@@ -188,7 +188,7 @@
self.w(u'</body>')
def nav_column(self, view, context):
- boxes = list(self._cw.vreg['boxes'].poss_visible_objects(
+ boxes = list(self._cw.vreg['ctxcomponents'].poss_visible_objects(
self._cw, rset=self.cw_rset, view=view, context=context))
if boxes:
getlayout = self._cw.vreg['components'].select
@@ -258,7 +258,7 @@
w(u'<table width="100%" height="100%" border="0"><tr>\n')
w(u'<td id="navColumnLeft">\n')
self.topleft_header()
- boxes = list(self._cw.vreg['boxes'].poss_visible_objects(
+ boxes = list(self._cw.vreg['ctxcomponents'].poss_visible_objects(
self._cw, rset=self.cw_rset, view=view, context='left'))
if boxes:
w(u'<div class="navboxes">\n')
@@ -409,7 +409,7 @@
def call(self, view, **kwargs):
"""by default, display informal messages in content header"""
- components = self._cw.vreg['contentnavigation'].poss_visible_objects(
+ components = self._cw.vreg['ctxcomponents'].poss_visible_objects(
self._cw, rset=self.cw_rset, view=view, context='navtop')
if components:
self.w(u'<div id="contentheader">')
@@ -425,7 +425,7 @@
__regid__ = 'contentfooter'
def call(self, view, **kwargs):
- components = self._cw.vreg['contentnavigation'].poss_visible_objects(
+ components = self._cw.vreg['ctxcomponents'].poss_visible_objects(
self._cw, rset=self.cw_rset, view=view, context='navbottom')
if components:
self.w(u'<div id="contentfooter">')
--- a/web/views/bookmark.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/bookmark.py Wed Aug 25 10:29:07 2010 +0200
@@ -24,7 +24,7 @@
from cubicweb import Unauthorized
from cubicweb.selectors import is_instance, one_line_rset
from cubicweb.web.htmlwidgets import BoxWidget, BoxMenu, RawBoxItem
-from cubicweb.web import action, box, uicfg, formwidgets as fw
+from cubicweb.web import action, component, uicfg, formwidgets as fw
from cubicweb.web.views import primary
_abaa = uicfg.actionbox_appearsin_addmenu
@@ -69,7 +69,7 @@
self.w(u'</div>')
-class BookmarksBox(box.Box):
+class BookmarksBox(component.CtxComponent):
"""display a box containing all user's bookmarks"""
__regid__ = 'bookmarks_box'
@@ -88,7 +88,7 @@
self.can_edit = (eschema.has_perm(self._cw, 'add') and
rschema.has_perm(self._cw, 'add', toeid=ueid))
if not self.bookmarks_rset and not self.can_edit:
- raise box.EmptyComponent()
+ raise component.EmptyComponent()
self.items = []
def render_body(self, w):
--- a/web/views/boxes.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/boxes.py Wed Aug 25 10:29:07 2010 +0200
@@ -25,6 +25,7 @@
* possible views box
* startup views box
"""
+from __future__ import with_statement
__docformat__ = "restructuredtext en"
_ = unicode
@@ -35,23 +36,25 @@
from logilab.common.deprecation import class_deprecated
from cubicweb import Unauthorized
-from cubicweb.selectors import (match_user_groups, match_context, match_kwargs,
- non_final_entity, nonempty_rset)
+from cubicweb.selectors import (match_user_groups, match_kwargs,
+ non_final_entity, nonempty_rset,
+ match_context, contextual)
+from cubicweb.utils import wrap_on_write
from cubicweb.view import EntityView
from cubicweb.schema import display_name
-from cubicweb.web import box, htmlwidgets
+from cubicweb.web import component, box, htmlwidgets
# XXX bw compat, some cubes import this class from here
BoxTemplate = box.BoxTemplate
BoxHtml = htmlwidgets.BoxHtml
-class EditBox(box.Box): # XXX rename to ActionsBox
+class EditBox(component.CtxComponent): # XXX rename to ActionsBox
"""
box with all actions impacting the entity displayed: edit, copy, delete
change state, add related entities
"""
__regid__ = 'edit_box'
- __select__ = box.Box.__select__ & non_final_entity()
+ __select__ = component.CtxComponent.__select__ & non_final_entity()
title = _('actions')
order = 2
@@ -89,7 +92,7 @@
for submenu in self._menus_in_order:
self.add_submenu(self, submenu)
if not self.items:
- raise box.EmptyComponent()
+ raise component.EmptyComponent()
def render_title(self, w):
title = self._cw._(self.title)
@@ -132,7 +135,7 @@
box.append(xml_escape(submenu.label))
-class SearchBox(box.Box):
+class SearchBox(component.CtxComponent):
"""display a box with a simple search form"""
__regid__ = 'search_box'
@@ -163,7 +166,7 @@
# boxes disabled by default ###################################################
-class PossibleViewsBox(box.Box):
+class PossibleViewsBox(component.CtxComponent):
"""display a box containing links to all possible views"""
__regid__ = 'possible_views_box'
@@ -176,7 +179,7 @@
rset=self.cw_rset)
if v.category != 'startupview']
if not self.views:
- raise box.EmptyComponent()
+ raise component.EmptyComponent()
self.items = []
def render_body(self, w):
@@ -200,11 +203,11 @@
self.views = [v for v in self._cw.vreg['views'].possible_views(self._cw)
if v.category == 'startupview']
if not self.views:
- raise box.EmptyComponent()
+ raise component.EmptyComponent()
self.items = []
-class RsetBox(box.Box):
+class RsetBox(component.CtxComponent):
"""helper view class to display an rset in a sidebox"""
__select__ = nonempty_rset() & match_kwargs('title', 'vid')
__regid__ = 'rsetbox'
@@ -214,6 +217,7 @@
@property
def domid(self):
return super(RsetBox, self).domid + unicode(abs(id(self)))
+
def render_title(self, w):
w(self.cw_extra_kwargs['title'])
@@ -225,43 +229,38 @@
class SideBoxView(EntityView):
"""helper view class to display some entities in a sidebox"""
__metaclass__ = class_deprecated
- __deprecation_warning__ = 'SideBoxView is deprecated, use RsetBox instead'
+ __deprecation_warning__ = '[3.10] SideBoxView is deprecated, use RsetBox instead (%(cls)s)'
__regid__ = 'sidebox'
def call(self, **kwargs):
"""display a list of entities by calling their <item_vid> view"""
- box = self._cw.vreg['boxes'].select('rsetbox', self._cw, rset=self.cw_rset,
- vid='autolimited', title=title,
- **self.cw_extra_kwargs)
+ box = self._cw.vreg['ctxcomponents'].select(
+ 'rsetbox', self._cw, rset=self.cw_rset, vid='autolimited',
+ title=title, **self.cw_extra_kwargs)
box.render(self.w)
-class ContextualBoxLayout(box.Layout):
- __select__ = match_context('incontext', 'left', 'right') & box.contextual()
+class ContextualBoxLayout(component.Layout):
+ __select__ = match_context('incontext', 'left', 'right') & contextual()
# predefined class in cubicweb.css: contextualBox | contextFreeBox
# XXX: navigationBox | actionBox
cssclass = 'contextualBox'
def render(self, w):
- view = self.cw_extra_kwargs['view']
- try:
- view.init_rendering()
- except Unauthorized, ex:
- self.warning("can't render %s: %s", view, ex)
- return
- except box.EmptyComponent:
- return
- w(u'<div class="%s %s" id="%s">' % (self.cssclass, view.cssclass,
- view.domid))
- w(u'<div class="boxTitle"><span>')
- view.render_title(w)
- w(u'</span></div>\n<div class="boxBody">')
- view.render_body(w)
- # boxFooter div is a CSS place holder (for shadow for example)
- w(u'</div><div class="boxFooter"></div></div>\n')
+ if self.init_rendering():
+ view = self.cw_extra_kwargs['view']
+ w(u'<div class="%s %s" id="%s">' % (self.cssclass, view.cssclass,
+ view.domid))
+ with wrap_on_write(w, u'<div class="boxTitle"><span>',
+ u'</span></div>') as wow:
+ view.render_title(wow)
+ w(u'<div class="boxBody">')
+ view.render_body(w)
+ # boxFooter div is a CSS place holder (for shadow for example)
+ w(u'</div><div class="boxFooter"></div></div>\n')
class ContextFreeBoxLayout(ContextualBoxLayout):
- __select__ = match_context('incontext', 'left', 'right') & ~box.contextual()
+ __select__ = match_context('incontext', 'left', 'right') & ~contextual()
cssclass = 'contextFreeBox'
--- a/web/views/cwproperties.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/cwproperties.py Wed Aug 25 10:29:07 2010 +0200
@@ -45,7 +45,7 @@
_('ui')
_('boxes')
_('components')
-_('contentnavigation')
+_('ctxcomponents')
_('navigation.combobox-limit')
_('navigation.page-size')
_('navigation.related-limit')
--- a/web/views/debug.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/debug.py Wed Aug 25 10:29:07 2010 +0200
@@ -150,6 +150,8 @@
self.w(u'<p>%s</p>\n' % ' - '.join('<a href="%s#%s">%s</a>'
% (url, key, key) for key in keys))
for key in keys:
+ if key in ('boxes', 'contentnavigation'): # those are bw compat registries
+ continue
self.w(u'<h2 id="%s">%s</h2>' % (key, key))
if self._cw.vreg[key]:
values = sorted(self._cw.vreg[key].iteritems())
--- a/web/views/facets.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/facets.py Wed Aug 25 10:29:07 2010 +0200
@@ -25,7 +25,7 @@
from cubicweb.selectors import (non_final_entity, multi_lines_rset,
match_context_prop, yes, relation_possible)
from cubicweb.utils import json_dumps
-from cubicweb.web import box
+from cubicweb.web import component
from cubicweb.web.facet import (AbstractFacet, FacetStringWidget, RelationFacet,
prepare_facets_rqlst, filter_hiddens, _cleanup_rqlst,
_prepare_vocabulary_rqlst)
@@ -38,12 +38,11 @@
return 0
-class FilterBox(box.Box):
+class FilterBox(component.CtxComponent):
"""filter results of a query"""
__regid__ = 'filter_box'
- __select__ = (((non_final_entity() & multi_lines_rset())
- | contextview_selector()
- ) & match_context_prop())
+ __select__ = ((non_final_entity() & multi_lines_rset())
+ | contextview_selector())
context = 'left' # XXX doesn't support 'incontext', only 'left' or 'right'
title = _('boxes_filter_box')
visible = True # functionality provided by the search box by default
--- a/web/views/ibreadcrumbs.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/ibreadcrumbs.py Wed Aug 25 10:29:07 2010 +0200
@@ -98,8 +98,8 @@
_('visible'): dict(type='Boolean', default=True,
help=_('display the component or not')),
}
- title = _('contentnavigation_breadcrumbs')
- help = _('contentnavigation_breadcrumbs_description')
+ # title = _('ctxcomponents_breadcrumbs')
+ # help = _('ctxcomponents_breadcrumbs_description')
separator = u' > '
link_template = u'<a href="%s">%s</a>'
--- a/web/views/idownloadable.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/idownloadable.py Wed Aug 25 10:29:07 2010 +0200
@@ -27,7 +27,7 @@
from cubicweb.selectors import (one_line_rset, is_instance, match_context_prop,
adaptable, has_mimetype)
from cubicweb.mttransforms import ENGINE
-from cubicweb.web import box, httpcache
+from cubicweb.web import component, httpcache
from cubicweb.web.views import primary, baseviews
@@ -47,10 +47,10 @@
w(u'</div></div>\n')
-class DownloadBox(box.EntityBox):
+class DownloadBox(component.EntityCtxComponent):
__regid__ = 'download_box'
# no download box for images
- __select__ = (box.EntityBox.__select__ &
+ __select__ = (component.EntityCtxComponent.__select__ &
adaptable('IDownloadable') & ~has_mimetype('image/'))
order = 10
--- a/web/views/navigation.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/navigation.py Wed Aug 25 10:29:07 2010 +0200
@@ -29,7 +29,7 @@
adaptable, implements)
from cubicweb.uilib import cut
from cubicweb.view import EntityAdapter, implements_adapter_compat
-from cubicweb.web.component import EntityVComponent, NavigationComponent
+from cubicweb.web.component import EmptyComponent, EntityCtxComponent, NavigationComponent
class PageNavigation(NavigationComponent):
@@ -201,59 +201,55 @@
raise NotImplementedError
-class NextPrevNavigationComponent(EntityVComponent):
+class NextPrevNavigationComponent(EntityCtxComponent):
__regid__ = 'prevnext'
# register msg not generated since no entity implements IPrevNext in cubicweb
# itself
- title = _('contentnavigation_prevnext')
- help = _('contentnavigation_prevnext_description')
- __select__ = EntityVComponent.__select__ & adaptable('IPrevNext')
+ help = _('ctxcomponents_prevnext_description')
+ __select__ = EntityCtxComponent.__select__ & adaptable('IPrevNext')
context = 'navbottom'
order = 10
- def call(self, view=None):
- self.cell_call(0, 0, view=view)
+ def init_rendering(self):
+ adapter = self.entity.cw_adapt_to('IPrevNext')
+ self.previous = adapter.previous_entity()
+ self.next = adapter.next_entity()
+ if not (self.previous or self.next):
+ raise EmptyComponent()
- def cell_call(self, row, col, view=None):
- entity = self.cw_rset.get_entity(row, col)
- adapter = entity.cw_adapt_to('IPrevNext')
- previous = adapter.previous_entity()
- next = adapter.next_entity()
- if previous or next:
- textsize = self._cw.property_value('navigation.short-line-size')
- self.w(u'<div class="prevnext">')
- if previous:
- self.previous_div(previous, textsize)
- if next:
- self.next_div(next, textsize)
- self.w(u'</div>')
- self.w(u'<div class="clear"></div>')
+ def render_body(self, w):
+ w(u'<div class="prevnext">')
+ self.prevnext(w)
+ w(u'</div>')
+ w(u'<div class="clear"></div>')
+
+ def prevnext(self, w):
+ if self.previous:
+ self.prevnext_entity(w, self.previous, 'prev')
+ if self.next:
+ self.prevnext_entity(w, self.next, 'next')
- def previous_div(self, previous, textsize):
- self.w(u'<div class="previousEntity left">')
- self.w(self.previous_link(previous, textsize))
- self.w(u'</div>')
- self._cw.html_headers.add_raw('<link rel="prev" href="%s" />'
- % xml_escape(previous.absolute_url()))
-
- def previous_link(self, previous, textsize):
- return u'<a href="%s" title="%s"><< %s</a>' % (
- xml_escape(previous.absolute_url()),
- self._cw._('i18nprevnext_previous'),
- xml_escape(cut(previous.dc_title(), textsize)))
+ def prevnext_entity(self, w, entity, type):
+ textsize = self._cw.property_value('navigation.short-line-size')
+ if type == 'prev':
+ title = self._cw._('i18nprevnext_previous')
+ icon = u'<< '
+ cssclass = u'previousEntity left'
+ else:
+ title = self._cw._('i18nprevnext_next')
+ icon = u'>> '
+ cssclass = u'nextEntity right'
+ self.prevnext_div(w, type, cssclass, entity.absolute_url(),
+ title, icon + xml_escape(cut(entity.dc_title(), textsize)))
- def next_div(self, next, textsize):
- self.w(u'<div class="nextEntity right">')
- self.w(self.next_link(next, textsize))
- self.w(u'</div>')
- self._cw.html_headers.add_raw('<link rel="next" href="%s" />'
- % xml_escape(next.absolute_url()))
-
- def next_link(self, next, textsize):
- return u'<a href="%s" title="%s">%s >></a>' % (
- xml_escape(next.absolute_url()),
- self._cw._('i18nprevnext_next'),
- xml_escape(cut(next.dc_title(), textsize)))
+ def prevnext_div(self, w, type, cssclass, url, title, content):
+ w(u'<div class="%s">' % cssclass)
+ w(u'<a href="%s" title="%s">%s</a>' % (xml_escape(url),
+ xml_escape(title),
+ content))
+ w(u'</div>')
+ self._cw.html_headers.add_raw('<link rel="%s" href="%s" />' % (
+ type, xml_escape(url)))
def do_paginate(view, rset=None, w=None, show_all_option=True, page_size=None):
--- a/web/views/primary.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/primary.py Wed Aug 25 10:29:07 2010 +0200
@@ -90,7 +90,7 @@
def content_navigation_components(self, context):
self.w(u'<div class="%s">' % context)
- for comp in self._cw.vreg['contentnavigation'].poss_visible_objects(
+ for comp in self._cw.vreg['ctxcomponents'].poss_visible_objects(
self._cw, rset=self.cw_rset, row=self.cw_row, view=self, context=context):
try:
comp.render(w=self.w, row=self.cw_row, view=self)
@@ -213,7 +213,7 @@
def _prepare_side_boxes(self, entity):
sideboxes = []
- boxesreg = self._cw.vreg['boxes']
+ boxesreg = self._cw.vreg['ctxcomponents']
for rschema, tschemas, role, dispctrl in self._section_def(entity, 'sideboxes'):
rset = self._relation_rset(entity, rschema, role, dispctrl)
if not rset:
@@ -344,6 +344,6 @@
_pvs = uicfg.primaryview_section
for rtype in ('eid', 'creation_date', 'modification_date', 'cwuri',
'is', 'is_instance_of', 'identity', 'owned_by', 'created_by',
- 'require_permission', 'see_also'):
+ 'require_permission'):
_pvs.tag_subject_of(('*', rtype, '*'), 'hidden')
_pvs.tag_object_of(('*', rtype, '*'), 'hidden')
--- a/web/views/workflow.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/workflow.py Wed Aug 25 10:29:07 2010 +0200
@@ -25,6 +25,7 @@
_ = unicode
import os
+from warnings import warn
from logilab.mtconverter import xml_escape
from logilab.common.graph import escape
@@ -160,15 +161,21 @@
displaycols=displaycols, headers=headers)
-class WFHistoryVComponent(component.EntityVComponent):
+class WFHistoryVComponent(component.CtxComponent):
"""display the workflow history for entities supporting it"""
__regid__ = 'wfhistory'
__select__ = WFHistoryView.__select__ & component.EntityVComponent.__select__
context = 'navcontentbottom'
title = _('Workflow history')
- def cell_call(self, row, col, view=None):
- self.wview('wfhistory', self.cw_rset, row=row, col=col, view=view)
+ def render_body(self, w):
+ if hasattr(self, 'cell_call'):
+ warn('[3.10] %s should now implement render_body instead of cell_call',
+ DeprecationWarning, self.__class__)
+ self.w = w
+ self.cell_call(self.entity.cw_row, self.entity.cw_col)
+ else:
+ self.entity.view('wfhistory', w=w)
# workflow actions #############################################################
--- a/web/views/xmlrss.py Wed Aug 25 10:01:11 2010 +0200
+++ b/web/views/xmlrss.py Wed Aug 25 10:29:07 2010 +0200
@@ -29,7 +29,7 @@
from cubicweb.view import EntityView, EntityAdapter, AnyRsetView, Component
from cubicweb.view import implements_adapter_compat
from cubicweb.uilib import simple_sgml_tag
-from cubicweb.web import httpcache, box
+from cubicweb.web import httpcache, component
# base xml views ##############################################################
@@ -148,10 +148,10 @@
return entity.cw_adapt_to('IFeed').rss_feed_url()
-class RSSIconBox(box.Box):
+class RSSIconBox(component.CtxComponent):
"""just display the RSS icon on uniform result set"""
__regid__ = 'rss'
- __select__ = (box.Box.__select__
+ __select__ = (component.CtxComponent.__select__
& appobject_selectable('components', 'rss_feed_url'))
visible = False