--- a/common/appobject.py Tue Feb 17 16:25:20 2009 +0100
+++ b/common/appobject.py Tue Feb 17 17:28:25 2009 +0100
@@ -23,7 +23,6 @@
from cubicweb.common.registerers import yes_registerer, priority_registerer
from cubicweb.common.selectors import yes
-_MARKER = object()
class Cache(dict):
@@ -73,6 +72,7 @@
instance = cls(req, rset)
instance.row = row
instance.col = col
+ instance.sel_kwargs = kwargs
return instance
# Eproperties definition:
@@ -323,56 +323,3 @@
self.req = req
self.rset = rset
self.__dict__.update(kwargs)
-
-
-class ReloadableMixIn(object):
- """simple mixin for reloadable parts of UI"""
-
- def user_callback(self, cb, args, msg=None, nonify=False):
- """register the given user callback and return an url to call it ready to be
- inserted in html
- """
- self.req.add_js('cubicweb.ajax.js')
- if nonify:
- _cb = cb
- def cb(*args):
- _cb(*args)
- cbname = self.req.register_onetime_callback(cb, *args)
- return self.build_js(cbname, html_escape(msg or ''))
-
- def build_update_js_call(self, cbname, msg):
- rql = html_escape(self.rset.printable_rql())
- return "javascript:userCallbackThenUpdateUI('%s', '%s', '%s', '%s', '%s', '%s')" % (
- cbname, self.id, rql, msg, self.__registry__, self.div_id())
-
- def build_reload_js_call(self, cbname, msg):
- return "javascript:userCallbackThenReloadPage('%s', '%s')" % (cbname, msg)
-
- build_js = build_update_js_call # expect updatable component by default
-
- def div_id(self):
- return ''
-
-
-class ComponentMixIn(ReloadableMixIn):
- """simple mixin for component object"""
- __registry__ = 'components'
- __registerer__ = yes_registerer
- __selectors__ = (yes,)
- __select__ = classmethod(*__selectors__)
-
- def div_class(self):
- return '%s %s' % (self.propval('htmlclass'), self.id)
-
- def div_id(self):
- return '%sComponent' % self.id
-
-
-class Component(ComponentMixIn, AppObject):
- """base class for non displayable components
- """
-
-class SingletonComponent(Component):
- """base class for non displayable unique components
- """
- __registerer__ = priority_registerer
--- a/common/registerers.py Tue Feb 17 16:25:20 2009 +0100
+++ b/common/registerers.py Tue Feb 17 17:28:25 2009 +0100
@@ -84,18 +84,6 @@
# remove it latter if no object is implementing accepted interfaces
if _accepts_interfaces(self.vobject):
return self.vobject
-# XXX no more .accepts attribute
-# if not 'Any' in self.vobject.accepts:
-# for ertype in self.vobject.accepts:
-# if ertype in self.schema:
-# break
-# else:
-# self.skip()
-# return None
- for required in getattr(self.vobject, 'requires', ()):
- if required not in self.schema:
- self.skip()
- return
self.remove_equivalents(registered)
return self.vobject
@@ -185,20 +173,6 @@
registerer = accepts_registerer(self.registry, cls)
cls = registerer.do_it_yourself(registered)
return cls
-
-
-class extresources_registerer(priority_registerer):
- """'registerer according to a .need_resources attributes which
- should list necessary resource identifiers for the wrapped object.
- If one of its resources is missing, don't register
- """
- def do_it_yourself(self, registered):
- if not hasattr(self.config, 'has_resource'):
- return
- for resourceid in self.vobject.need_resources:
- if not self.config.has_resource(resourceid):
- return
- return super(extresources_registerer, self).do_it_yourself(registered)
__all__ = [cls.__name__ for cls in globals().values()
--- a/common/view.py Tue Feb 17 16:25:20 2009 +0100
+++ b/common/view.py Tue Feb 17 17:28:25 2009 +0100
@@ -69,6 +69,8 @@
STRICT_DOCTYPE = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" %s>\n'
+# base view object ############################################################
+
class View(AppRsetObject):
"""abstract view class, used as base for every renderable object such
as views, templates, some components...web
@@ -418,7 +420,7 @@
is only used globally (i.e. no result set adaptation)
"""
__registry__ = 'templates'
- __registerer__ = priority_registerer
+ # __registerer__ = priority_registerer
__selectors__ = (yes,)
registered = require_group_compat(View.registered.im_func)
@@ -466,3 +468,50 @@
self._stream.doctype = self.doctype
if not xmldecl:
self._stream.xmldecl = u''
+
+# concrete component base classes #############################################
+
+class ReloadableMixIn(object):
+ """simple mixin for reloadable parts of UI"""
+
+ def user_callback(self, cb, args, msg=None, nonify=False):
+ """register the given user callback and return an url to call it ready to be
+ inserted in html
+ """
+ self.req.add_js('cubicweb.ajax.js')
+ if nonify:
+ _cb = cb
+ def cb(*args):
+ _cb(*args)
+ cbname = self.req.register_onetime_callback(cb, *args)
+ return self.build_js(cbname, html_escape(msg or ''))
+
+ def build_update_js_call(self, cbname, msg):
+ rql = html_escape(self.rset.printable_rql())
+ return "javascript:userCallbackThenUpdateUI('%s', '%s', '%s', '%s', '%s', '%s')" % (
+ cbname, self.id, rql, msg, self.__registry__, self.div_id())
+
+ def build_reload_js_call(self, cbname, msg):
+ return "javascript:userCallbackThenReloadPage('%s', '%s')" % (cbname, msg)
+
+ build_js = build_update_js_call # expect updatable component by default
+
+ def div_id(self):
+ return ''
+
+
+class Component(ReloadableMixIn, View):
+ """base class for components"""
+ __registry__ = 'components'
+ __registerer__ = yes_registerer
+ __selectors__ = (yes,)
+ property_defs = {
+ _('visible'): dict(type='Boolean', default=True,
+ help=_('display the box or not')),
+ }
+
+ def div_class(self):
+ return '%s %s' % (self.propval('htmlclass'), self.id)
+
+ def div_id(self):
+ return '%sComponent' % self.id
--- a/cwvreg.py Tue Feb 17 16:25:20 2009 +0100
+++ b/cwvreg.py Tue Feb 17 17:28:25 2009 +0100
@@ -48,8 +48,10 @@
def reset(self):
self._registries = {}
self._lastmodifs = {}
- # two special registries, propertydefs which care all the property definitions, and
- # propertyvals which contains values for those properties
+ self._needs_iface = {}
+ # two special registries, propertydefs which care all the property
+ # definitions, and propertyvals which contains values for those
+ # properties
self._registries['propertydefs'] = {}
self._registries['propertyvalues'] = self.eprop_values = {}
for key, propdef in self.config.eproperty_definitions():
@@ -73,42 +75,42 @@
for objects in regcontent.values():
for obj in objects:
obj.schema = schema
+
+ def register_if_interface_found(self, obj, ifaces, **kwargs):
+ """register an object but remove it if no entity class implements one of
+ the given interfaces
+ """
+ if not isinstance(ifaces, (tuple, list)):
+ self._needs_iface[obj] = frozenset((ifaces,))
+ else:
+ self._needs_iface[obj] = frozenset(ifaces)
+ self.register(obj, **kwargs)
+
+ def register(self, obj, **kwargs):
+ super(CubicWebRegistry, self).register(obj, **kwargs)
+ # XXX bw compat
+ ifaces = getattr(obj, 'accepts_interfaces', None)
+ if ifaces:
+ self._needs_iface[obj] = frozenset(ifaces)
def register_objects(self, path, force_reload=None):
- """overriden to handle type class cache issue"""
- if super(CubicWebRegistry, self).register_objects(path, force_reload):
+ """overriden to remove objects requiring a missing interface"""
+ if super(CubicWebRegistry, self).register_objects(path, force_reload):
# clear etype cache if you don't want to run into deep weirdness
clear_cache(self, 'etype_class')
+ # we may want to keep interface dependent objects (e.g.for i18n
+ # catalog generation)
+ if not self.config.cleanup_interface_sobjects:
+ return
# remove vobjects that don't support any available interface
interfaces = set()
for classes in self.get('etypes', {}).values():
for cls in classes:
interfaces.update(cls.__implements__)
- if not self.config.cleanup_interface_sobjects:
- return
- for registry, regcontent in self._registries.items():
- if registry in ('propertydefs', 'propertyvalues', 'etypes'):
- continue
- for oid, objects in regcontent.items():
- for obj in reversed(objects[:]):
- if not obj in objects:
- continue # obj has been kicked by a previous one
- accepted = set(getattr(obj, 'accepts_interfaces', ()))
- if accepted:
- for accepted_iface in accepted:
- for found_iface in interfaces:
- if issubclass(found_iface, accepted_iface):
- # consider priority if necessary
- if hasattr(obj.__registerer__, 'remove_all_equivalents'):
- registerer = obj.__registerer__(self, obj)
- registerer.remove_all_equivalents(objects)
- break
- else:
- self.debug('kicking vobject %s (unsupported interface)', obj)
- objects.remove(obj)
- # if objects is empty, remove oid from registry
- if not objects:
- del regcontent[oid]
+ for obj, ifaces in self._needs_iface.items():
+ if not ifaces & interfaces:
+ self.debug('kicking vobject %s (unsupported interface)', obj)
+ self.unregister(obj)
def eid_rset(self, cursor, eid, etype=None):
"""return a result set for the given eid without doing actual query
--- a/selectors.py Tue Feb 17 16:25:20 2009 +0100
+++ b/selectors.py Tue Feb 17 17:28:25 2009 +0100
@@ -483,12 +483,16 @@
self.expected_ifaces = expected_ifaces
def score_class(self, eclass, req):
+ print '***********************************'
score = 0
for iface in self.expected_ifaces:
+ print 'TESTING', iface, 'on', eclass
if isinstance(iface, basestring):
# entity type
iface = eclass.vreg.etype_class(iface)
+ print 'found iface ===', iface
if implements_iface(eclass, iface):
+ print 'and implementing !!!'
score += 1
if getattr(iface, '__registry__', None) == 'etypes':
score += 1
@@ -504,6 +508,7 @@
score += index
# print 'etype majoration', index
break
+ print '***********************************', score
return score
--- a/sobjects/notification.py Tue Feb 17 16:25:20 2009 +0100
+++ b/sobjects/notification.py Tue Feb 17 17:28:25 2009 +0100
@@ -18,8 +18,7 @@
from logilab.common.textutils import normalize_text
from cubicweb import RegistryException
-from cubicweb.common.view import EntityView
-from cubicweb.common.appobject import Component
+from cubicweb.common.view import EntityView, Component
from cubicweb.common.registerers import accepts_registerer
from cubicweb.common.selectors import implements
from cubicweb.common.mail import format_mail
--- a/sobjects/supervising.py Tue Feb 17 16:25:20 2009 +0100
+++ b/sobjects/supervising.py Tue Feb 17 17:28:25 2009 +0100
@@ -8,7 +8,7 @@
__docformat__ = "restructuredtext en"
from cubicweb import UnknownEid
-from cubicweb.common.appobject import ComponentMixIn
+from cubicweb.common.appobject import Component
from cubicweb.common.view import StartupView
from cubicweb.common.mail import format_mail
from cubicweb.server.hooksmanager import Hook
@@ -138,9 +138,10 @@
yield change
-class SupervisionEmailView(ComponentMixIn, StartupView):
+class SupervisionEmailView(Component):
"""view implementing the email API for data changes supervision notification
"""
+ __selectors__ = (none_rset,)
id = 'supervision_notif'
def recipients(self):
--- a/vregistry.py Tue Feb 17 16:25:20 2009 +0100
+++ b/vregistry.py Tue Feb 17 17:28:25 2009 +0100
@@ -204,109 +204,6 @@
def __contains__(self, key):
return key in self._registries
-
- def register_vobject_class(self, cls, _kicked=set()):
- """handle vobject class registration
-
- vobject class with __abstract__ == True in their local dictionnary or
- with a name starting starting by an underscore are not registered.
- Also a vobject class needs to have __registry__ and id attributes set
- to a non empty string to be registered.
-
- Registration is actually handled by vobject's registerer.
- """
- if (cls.__dict__.get('__abstract__') or cls.__name__[0] == '_'
- or not cls.__registry__ or not cls.id):
- return
- # while reloading a module :
- # if cls was previously kicked, it means that there is a more specific
- # vobject defined elsewhere re-registering cls would kick it out
- if cls.classid() in _kicked:
- self.debug('not re-registering %s because it was previously kicked',
- cls.classid())
- else:
- regname = cls.__registry__
- if cls.id in self.config['disable-%s' % regname]:
- return
- registry = self._registries.setdefault(regname, {})
- vobjects = registry.setdefault(cls.id, [])
- registerer = cls.__registerer__(self, cls)
- cls = registerer.do_it_yourself(vobjects)
- #_kicked |= registerer.kicked
- if cls:
- # registered() is technically a classmethod but is not declared
- # as such because we need to compose registered in some cases
- vobject = cls.registered.im_func(cls, self)
- try:
- vname = vobject.__name__
- except AttributeError:
- vname = vobject.__class__.__name__
- self.debug('registered vobject %s in registry %s with id %s',
- vname, cls.__registry__, cls.id)
- vobjects.append(vobject)
-
- def unregister_module_vobjects(self, modname):
- """removes registered objects coming from a given module
-
- returns a dictionnary classid/class of all classes that will need
- to be updated after reload (i.e. vobjects referencing classes defined
- in the <modname> module)
- """
- unregistered = {}
- # browse each registered object
- for registry, objdict in self.items():
- for oid, objects in objdict.items():
- for obj in objects[:]:
- objname = obj.classid()
- # if the vobject is defined in this module, remove it
- if objname.startswith(modname):
- unregistered[objname] = obj
- objects.remove(obj)
- self.debug('unregistering %s in %s registry',
- objname, registry)
- # if not, check if the vobject can be found in baseclasses
- # (because we also want subclasses to be updated)
- else:
- if not isinstance(obj, type):
- obj = obj.__class__
- for baseclass in obj.__bases__:
- if hasattr(baseclass, 'classid'):
- baseclassid = baseclass.classid()
- if baseclassid.startswith(modname):
- unregistered[baseclassid] = baseclass
- # update oid entry
- if objects:
- objdict[oid] = objects
- else:
- del objdict[oid]
- return unregistered
-
-
- def update_registered_subclasses(self, oldnew_mapping):
- """updates subclasses of re-registered vobjects
-
- if baseviews.PrimaryView is changed, baseviews.py will be reloaded
- automatically and the new version of PrimaryView will be registered.
- But all existing subclasses must also be notified of this change, and
- that's what this method does
-
- :param oldnew_mapping: a dict mapping old version of a class to
- the new version
- """
- # browse each registered object
- for objdict in self.values():
- for objects in objdict.values():
- for obj in objects:
- if not isinstance(obj, type):
- obj = obj.__class__
- # build new baseclasses tuple
- newbases = tuple(oldnew_mapping.get(baseclass, baseclass)
- for baseclass in obj.__bases__)
- # update obj's baseclasses tuple (__bases__) if needed
- if newbases != obj.__bases__:
- self.debug('updating %s.%s base classes',
- obj.__module__, obj.__name__)
- obj.__bases__ = newbases
def registry(self, name):
"""return the registry (dictionary of class objects) associated to
@@ -332,7 +229,69 @@
for objs in registry.values():
result += objs
return result
-
+
+ def object_by_id(self, registry, cid, *args, **kwargs):
+ """return the most specific component according to the resultset"""
+ objects = self[registry][cid]
+ assert len(objects) == 1, objects
+ return objects[0].selected(*args, **kwargs)
+
+ # methods for explicit (un)registration ###################################
+
+ def register(self, obj, registryname=None, oid=None):
+ """base method to add an object in the registry"""
+ registryname = registryname or obj.__registry__
+ oid = oid or obj.id
+ registry = self._registries.setdefault(registryname, {})
+ vobjects = registry.setdefault(oid, [])
+ # registered() is technically a classmethod but is not declared
+ # as such because we need to compose registered in some cases
+ vobject = obj.registered.im_func(cls, self)
+ assert not vobject in vobjects
+ vobjects.append(vobject)
+ try:
+ vname = vobject.__name__
+ except AttributeError:
+ vname = vobject.__class__.__name__
+ self.debug('registered vobject %s in registry %s with id %s',
+ vname, registryname, oid)
+ # automatic reloading management
+ self._registered['%s.%s' % (obj.__module__, oid)] = obj
+
+ def unregister(self, obj, registryname=None):
+ registryname = registryname or obj.__registry__
+ registry = self.registry(registryname)
+ removed_id = obj.classid()
+ for registered in registry[obj.id]:
+ # use classid() to compare classes because vreg will probably
+ # have its own version of the class, loaded through execfile
+ if registered.classid() == removed_id:
+ # XXX automatic reloading management
+ try:
+ registry[obj.id].remove(registered)
+ except ValueError:
+ self.warning('can\'t remove %s, no id %s in the %s registry',
+ removed_id, obj.id, registryname)
+ except ValueError:
+ self.warning('can\'t remove %s, not in the %s registry with id %s',
+ removed_id, registryname, obj.id)
+# else:
+# # if objects is empty, remove oid from registry
+# if not registry[obj.id]:
+# del regcontent[oid]
+ break
+
+ def register_and_replace(self, obj, replaced, registryname=None):
+ registryname = registryname or obj.__registry__
+ registry = self.registry(registryname)
+ registered_objs = registry[obj.id]
+ for index, registered in enumerate(registered_objs):
+ if registered.classid() == replaced:
+ registry[obj.id][index] = obj
+ self._registered['%s.%s' % (obj.__module__, obj.id)] = obj
+
+ # dynamic selection methods ###############################################
+
def select(self, vobjects, *args, **kwargs):
"""return an instance of the most specific object according
to parameters
@@ -374,15 +333,8 @@
def select_object(self, registry, cid, *args, **kwargs):
"""return the most specific component according to the resultset"""
return self.select(self.registry_objects(registry, cid), *args, **kwargs)
-
- def object_by_id(self, registry, cid, *args, **kwargs):
- """return the most specific component according to the resultset"""
- objects = self[registry][cid]
- assert len(objects) == 1, objects
- return objects[0].selected(*args, **kwargs)
# intialization methods ###################################################
-
def register_objects(self, path, force_reload=None):
if force_reload is None:
@@ -473,15 +425,18 @@
return True
def load_module(self, module):
- registered = {}
- self.info('loading %s', module)
- for objname, obj in vars(module).items():
- if objname.startswith('_'):
- continue
- self.load_ancestors_then_object(module.__name__, registered, obj)
- return registered
+ self._registered = {}
+ if hasattr(module, 'cw_register_objects'):
+ module.cw_register_objects(self)
+ else:
+ self.info('loading %s', module)
+ for objname, obj in vars(module).items():
+ if objname.startswith('_'):
+ continue
+ self.load_ancestors_then_object(module.__name__, obj)
+ return self._registered
- def load_ancestors_then_object(self, modname, registered, obj):
+ def load_ancestors_then_object(self, modname, obj):
# skip imported classes
if getattr(obj, '__module__', None) != modname:
return
@@ -492,11 +447,11 @@
except TypeError:
return
objname = '%s.%s' % (modname, obj.__name__)
- if objname in registered:
+ if objname in self._registered:
return
- registered[objname] = obj
+ self._registered[objname] = obj
for parent in obj.__bases__:
- self.load_ancestors_then_object(modname, registered, parent)
+ self.load_ancestors_then_object(modname, parent)
self.load_object(obj)
def load_object(self, obj):
@@ -507,6 +462,93 @@
raise
self.exception('vobject %s registration failed: %s', obj, ex)
+ # old automatic registration XXX deprecated ###############################
+
+ def register_vobject_class(self, cls):
+ """handle vobject class registration
+
+ vobject class with __abstract__ == True in their local dictionnary or
+ with a name starting starting by an underscore are not registered.
+ Also a vobject class needs to have __registry__ and id attributes set
+ to a non empty string to be registered.
+
+ Registration is actually handled by vobject's registerer.
+ """
+ if (cls.__dict__.get('__abstract__') or cls.__name__[0] == '_'
+ or not cls.__registry__ or not cls.id):
+ return
+ regname = cls.__registry__
+ if cls.id in self.config['disable-%s' % regname]:
+ return
+ registry = self._registries.setdefault(regname, {})
+ vobjects = registry.setdefault(cls.id, [])
+ registerer = cls.__registerer__(self, cls)
+ cls = registerer.do_it_yourself(vobjects)
+ if cls:
+ self.register(cls)
+
+ def unregister_module_vobjects(self, modname):
+ """removes registered objects coming from a given module
+
+ returns a dictionnary classid/class of all classes that will need
+ to be updated after reload (i.e. vobjects referencing classes defined
+ in the <modname> module)
+ """
+ unregistered = {}
+ # browse each registered object
+ for registry, objdict in self.items():
+ for oid, objects in objdict.items():
+ for obj in objects[:]:
+ objname = obj.classid()
+ # if the vobject is defined in this module, remove it
+ if objname.startswith(modname):
+ unregistered[objname] = obj
+ objects.remove(obj)
+ self.debug('unregistering %s in %s registry',
+ objname, registry)
+ # if not, check if the vobject can be found in baseclasses
+ # (because we also want subclasses to be updated)
+ else:
+ if not isinstance(obj, type):
+ obj = obj.__class__
+ for baseclass in obj.__bases__:
+ if hasattr(baseclass, 'classid'):
+ baseclassid = baseclass.classid()
+ if baseclassid.startswith(modname):
+ unregistered[baseclassid] = baseclass
+ # update oid entry
+ if objects:
+ objdict[oid] = objects
+ else:
+ del objdict[oid]
+ return unregistered
+
+ def update_registered_subclasses(self, oldnew_mapping):
+ """updates subclasses of re-registered vobjects
+
+ if baseviews.PrimaryView is changed, baseviews.py will be reloaded
+ automatically and the new version of PrimaryView will be registered.
+ But all existing subclasses must also be notified of this change, and
+ that's what this method does
+
+ :param oldnew_mapping: a dict mapping old version of a class to
+ the new version
+ """
+ # browse each registered object
+ for objdict in self.values():
+ for objects in objdict.values():
+ for obj in objects:
+ if not isinstance(obj, type):
+ obj = obj.__class__
+ # build new baseclasses tuple
+ newbases = tuple(oldnew_mapping.get(baseclass, baseclass)
+ for baseclass in obj.__bases__)
+ # update obj's baseclasses tuple (__bases__) if needed
+ if newbases != obj.__bases__:
+ self.debug('updating %s.%s base classes',
+ obj.__module__, obj.__name__)
+ obj.__bases__ = newbases
+
# init logging
set_log_methods(VObject, getLogger('cubicweb'))
set_log_methods(VRegistry, getLogger('cubicweb.registry'))
--- a/web/action.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/action.py Tue Feb 17 17:28:25 2009 +0100
@@ -6,8 +6,6 @@
"""
__docformat__ = "restructuredtext en"
-from logilab.common.deprecation import class_moved
-
from cubicweb import role, target
from cubicweb.selectors import (relation_possible, match_search_state,
one_line_rset, may_add_relation, yes,
--- a/web/application.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/application.py Tue Feb 17 17:28:25 2009 +0100
@@ -18,13 +18,13 @@
from cubicweb.cwvreg import CubicWebRegistry
from cubicweb.web import (LOGGER, StatusResponse, DirectResponse, Redirect, NotFound,
RemoteCallFailed, ExplicitLogin, InvalidSession)
-from cubicweb.web.component import SingletonComponent
+from cubicweb.web.component import Component
# make session manager available through a global variable so the debug view can
# print information about web session
SESSION_MANAGER = None
-class AbstractSessionManager(SingletonComponent):
+class AbstractSessionManager(Component):
"""manage session data associated to a session identifier"""
id = 'sessionmanager'
@@ -87,7 +87,7 @@
raise NotImplementedError()
-class AbstractAuthenticationManager(SingletonComponent):
+class AbstractAuthenticationManager(Component):
"""authenticate user associated to a request and check session validity"""
id = 'authmanager'
--- a/web/box.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/box.py Tue Feb 17 17:28:25 2009 +0100
@@ -17,8 +17,7 @@
accepts_registerer, extresources_registerer,
etype_rtype_priority_registerer)
#etype_rtype_selector, has_relation,
-from cubicweb.common.view import Template
-from cubicweb.common.appobject import ReloadableMixIn
+from cubicweb.common.view import Template, ReloadableMixIn
from cubicweb.web.htmlwidgets import (BoxLink, BoxWidget, SideBoxWidget,
RawBoxItem, BoxSeparator)
@@ -139,14 +138,6 @@
return (self.rql, {'x': self.req.user.eid}, 'x')
-class ExtResourcesBoxTemplate(BoxTemplate):
- """base class for boxes displaying external resources such as the RSS logo.
- It should list necessary resources with the .need_resources attribute.
- """
- __registerer__ = extresources_registerer
- need_resources = ()
-
-
class EntityBoxTemplate(BoxTemplate):
"""base class for boxes related to a single entity"""
__registerer__ = accepts_registerer
--- a/web/component.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/component.py Tue Feb 17 17:28:25 2009 +0100
@@ -6,10 +6,12 @@
"""
__docformat__ = "restructuredtext en"
+from logilab.common.deprecation import class_moved
+
from cubicweb.selectors import (
paginated_rset, one_line_rset, primary_view, match_context_prop,
condition_compat, accepts_compat, has_relation_compat)
-from cubicweb.common.appobject import Component, SingletonComponent, ComponentMixIn
+from cubicweb.common.appobject import Component
from cubicweb.common.utils import merge_dicts
from cubicweb.common.view import View
from cubicweb.common.registerers import action_registerer
@@ -17,13 +19,7 @@
_ = unicode
-class VComponent(ComponentMixIn, View):
- property_defs = {
- _('visible'): dict(type='Boolean', default=True,
- help=_('display the box or not')),
- }
-
-class EntityVComponent(VComponent):
+class EntityVComponent(Component):
"""abstract base class for additinal components displayed in content
headers and footer according to:
@@ -62,7 +58,7 @@
raise NotImplementedError()
-class NavigationComponent(ComponentMixIn, View):
+class NavigationComponent(Component):
"""abstract base class for navigation components"""
id = 'navigation'
__selectors__ = (paginated_rset,)
@@ -175,3 +171,9 @@
self.w(u'<div class="%s">' % self.div_class())
self.wview(self.vid, rset, title=self.req._(self.title).capitalize())
self.w(u'</div>')
+
+
+VComponent = class_moved('VComponent', VComponent,
+ 'VComponent is deprecated, use Component')
+SingletonVComponent = class_moved('SingletonVComponent', VComponent,
+ 'SingletonVComponent is deprecated, use Component and explicit registration control')
--- a/web/request.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/request.py Tue Feb 17 17:28:25 2009 +0100
@@ -1,7 +1,7 @@
"""abstract class for http request
:organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
__docformat__ = "restructuredtext en"
@@ -18,15 +18,15 @@
from logilab.common.decorators import cached
-# XXX move _MARKER here once AppObject.external_resource has been removed
from cubicweb.dbapi import DBAPIRequest
-from cubicweb.common.appobject import _MARKER
from cubicweb.common.mail import header
from cubicweb.common.uilib import remove_html_tags
from cubicweb.common.utils import SizeConstrainedList, HTMLHead
from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit, RequestError,
StatusResponse)
+_MARKER = object()
+
def list_form_param(form, param, pop=False):
"""get param from form parameters and return its value as a list,
--- a/web/views/actions.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/views/actions.py Tue Feb 17 17:28:25 2009 +0100
@@ -143,7 +143,7 @@
class DeleteAction(Action):
id = 'delete'
- __selectors__ = (has_permission('delete'),)
+ __selectors__ = (one_line_rset, has_permission('delete'))
title = _('delete')
category = 'moreactions'
@@ -158,7 +158,7 @@
class CopyAction(Action):
id = 'copy'
- __selectors__ = (has_permission('add'),)
+ __selectors__ = (one_line_rset, has_permission('add'))
title = _('copy')
category = 'moreactions'
--- a/web/views/apacherewrite.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/views/apacherewrite.py Tue Feb 17 17:28:25 2009 +0100
@@ -2,7 +2,7 @@
are much more limited for the moment)
:organization: Logilab
-:copyright: 2007-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
@@ -11,7 +11,7 @@
from re import compile
from cubicweb.web import Redirect
-from cubicweb.web.component import SingletonComponent
+from cubicweb.web.component import Component
class RewriteCond(object):
def __init__(self, condition, match='host', rules=(), action='rewrite'):
@@ -46,7 +46,7 @@
return path
-class ApacheURLRewrite(SingletonComponent):
+class ApacheURLRewrite(Component):
"""inherit from this class with actual rules to activate apache style rewriting
rules should have the form :
--- a/web/views/basecomponents.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/views/basecomponents.py Tue Feb 17 17:28:25 2009 +0100
@@ -20,13 +20,13 @@
match_form_params)
from cubicweb.web.htmlwidgets import MenuWidget, PopupBoxMenu, BoxSeparator, BoxLink
-from cubicweb.web.component import (VComponent, EntityVComponent,
+from cubicweb.web.component import (Component, EntityVComponent,
RelatedObjectsVComponent)
_ = unicode
-class RQLInputForm(VComponent):
+class RQLInputForm(Component):
"""build the rql input form, usually displayed in the header"""
id = 'rqlinput'
visible = False
@@ -55,7 +55,7 @@
self.w(u'</form></div>')
-class ApplLogo(VComponent):
+class ApplLogo(Component):
"""build the application logo, usually displayed in the header"""
id = 'logo'
site_wide = True # don't want user to hide this component using an eproperty
@@ -64,7 +64,7 @@
% (self.req.base_url(), self.req.external_resource('LOGO')))
-class ApplHelp(VComponent):
+class ApplHelp(Component):
"""build the help button, usually displayed in the header"""
id = 'help'
def call(self):
@@ -73,7 +73,7 @@
self.req._(u'help'),))
-class UserLink(VComponent):
+class UserLink(Component):
"""if the user is the anonymous user, build a link to login
else a link to the connected user object with a loggout link
"""
@@ -114,7 +114,7 @@
% (self.build_url('login'), self.req._('login')))
-class ApplicationMessage(VComponent):
+class ApplicationMessage(Component):
"""display application's messages given using the __message parameter
into a special div section
"""
@@ -169,7 +169,7 @@
displaycols=displaycols, headers=headers)
-class ApplicationName(VComponent):
+class ApplicationName(Component):
"""display the application name"""
id = 'appliname'
@@ -190,7 +190,7 @@
help = _('contentnavigation_seealso_description')
-class EtypeRestrictionComponent(VComponent):
+class EtypeRestrictionComponent(Component):
"""displays the list of entity types contained in the resultset
to be able to filter accordingly.
"""
@@ -240,14 +240,14 @@
-class RSSFeedURL(VComponent):
+class RSSFeedURL(Component):
id = 'rss_feed_url'
__selectors__ = (non_final_entity(),)
def feed_url(self):
return self.build_url(rql=self.limited_rql(), vid='rss')
-class RSSEntityFeedURL(VComponent):
+class RSSEntityFeedURL(Component):
id = 'rss_feed_url'
__selectors__ = (non_final_entity(), one_line_rset)
--- a/web/views/boxes.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/views/boxes.py Tue Feb 17 17:28:25 2009 +0100
@@ -19,7 +19,7 @@
from cubicweb.selectors import any_rset, appobject_selectable, match_user_groups
from cubicweb.web.htmlwidgets import BoxWidget, BoxMenu, BoxHtml, RawBoxItem
-from cubicweb.web.box import BoxTemplate, ExtResourcesBoxTemplate
+from cubicweb.web.box import BoxTemplate
_ = unicode
@@ -136,7 +136,6 @@
visible = True # enabled by default
title = _('search')
order = 0
- need_resources = 'SEARCH_GO'
formdef = u"""<form action="%s">
<table id="tsearch"><tr><td>
<input id="norql" type="text" accesskey="q" tabindex="%s" title="search text" value="%s" name="rql" />
@@ -186,19 +185,22 @@
box.render(self.w)
-class RSSIconBox(ExtResourcesBoxTemplate):
+class RSSIconBox(BoxTemplate):
"""just display the RSS icon on uniform result set"""
- __selectors__ = ExtResourcesBoxTemplate.__selectors__ + (appobject_selectable('components', 'rss_feed_url'),)
+ id = 'rss'
+ __selectors__ = BoxTemplate.__selectors__ + (appobject_selectable('components', 'rss_feed_url'),)
- id = 'rss'
order = 999
- need_resources = 'RSS_LOGO',
visible = False
def call(self, **kwargs):
+ try:
+ rss = self.req.external_resource('RSS_LOGO')
+ except KeyError:
+ self.error('missing RSS_LOGO external resource')
+ return
urlgetter = self.vreg.select_component('rss_feed_url', self.req, self.rset)
url = urlgetter.feed_url()
- rss = self.req.external_resource('RSS_LOGO')
self.w(u'<a href="%s"><img src="%s" alt="rss"/></a>\n' % (html_escape(url), rss))
--- a/web/views/facets.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/views/facets.py Tue Feb 17 17:28:25 2009 +0100
@@ -27,8 +27,7 @@
class FilterBox(BoxTemplate):
"""filter results of a query"""
id = 'filter_box'
- __selectors__ = (chainfirst(contextview_selector,
- chainall(non_final_entity, two_lines_rset)),
+ __selectors__ = ((non_final_entity() & two_lines_rset) | contextview_selector,
match_context_prop)
context = 'left'
title = _('boxes_filter_box')
--- a/web/views/magicsearch.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/views/magicsearch.py Tue Feb 17 17:28:25 2009 +0100
@@ -15,7 +15,7 @@
from rql.nodes import Relation
from cubicweb import Unauthorized
-from cubicweb.common.appobject import Component, SingletonComponent
+from cubicweb.common.appobject import Component
LOGGER = getLogger('cubicweb.magicsearch')
@@ -349,7 +349,7 @@
-class MagicSearchComponent(SingletonComponent):
+class MagicSearchComponent(Component):
id = 'magicsearch'
def __init__(self, req, rset=None):
super(MagicSearchComponent, self).__init__(req, rset)
--- a/web/views/tableview.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/views/tableview.py Tue Feb 17 17:28:25 2009 +0100
@@ -5,6 +5,8 @@
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
+from __future__ import with_statement
+
__docformat__ = "restructuredtext en"
from simplejson import dumps
@@ -222,7 +224,9 @@
def render(self, cellvid, row, col, w):
- self.view('cell', self.rset, row=row, col=col, cellvid=cellvid, w=w)
+ from cubicweb.selectors import traced_selection
+ with traced_selection( ('cell',) ):
+ self.view('cell', self.rset, row=row, col=col, cellvid=cellvid, w=w)
def get_rows(self):
return self.rset
--- a/web/views/urlpublishing.py Tue Feb 17 16:25:20 2009 +0100
+++ b/web/views/urlpublishing.py Tue Feb 17 17:28:25 2009 +0100
@@ -18,7 +18,7 @@
because of redirecting instead of direct traversal
:organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
@@ -28,7 +28,7 @@
from cubicweb import RegistryException, typed_eid
from cubicweb.web import NotFound, Redirect
-from cubicweb.web.component import SingletonComponent, Component
+from cubicweb.web.component import Component, Component
class PathDontMatch(Exception):
@@ -36,7 +36,7 @@
a path
"""
-class URLPublisherComponent(SingletonComponent):
+class URLPublisherComponent(Component):
"""associate url's path to view identifier / rql queries,
by applying a chain of urlpathevaluator components.