# HG changeset patch # User Adrien Di Mascio # Date 1234888105 -3600 # Node ID 9b04bbfe3f90133395b0811565827b8583b65abe # Parent 15fe1d3694d1585ab4693a1708c960af7dc5e732# Parent eab61515d76a0007179f14f198e4329defc61874 merge heads in tls-sprint branch diff -r eab61515d76a -r 9b04bbfe3f90 common/appobject.py --- 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 diff -r eab61515d76a -r 9b04bbfe3f90 common/registerers.py --- 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() diff -r eab61515d76a -r 9b04bbfe3f90 common/view.py --- 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'\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 diff -r eab61515d76a -r 9b04bbfe3f90 cwvreg.py --- 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 diff -r eab61515d76a -r 9b04bbfe3f90 selectors.py --- 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 diff -r eab61515d76a -r 9b04bbfe3f90 sobjects/notification.py --- 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 diff -r eab61515d76a -r 9b04bbfe3f90 sobjects/supervising.py --- 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): diff -r eab61515d76a -r 9b04bbfe3f90 vregistry.py --- 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 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 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')) diff -r eab61515d76a -r 9b04bbfe3f90 web/action.py --- 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, diff -r eab61515d76a -r 9b04bbfe3f90 web/application.py --- 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' diff -r eab61515d76a -r 9b04bbfe3f90 web/box.py --- 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 diff -r eab61515d76a -r 9b04bbfe3f90 web/component.py --- 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'
' % self.div_class()) self.wview(self.vid, rset, title=self.req._(self.title).capitalize()) self.w(u'
') + + +VComponent = class_moved('VComponent', VComponent, + 'VComponent is deprecated, use Component') +SingletonVComponent = class_moved('SingletonVComponent', VComponent, + 'SingletonVComponent is deprecated, use Component and explicit registration control') diff -r eab61515d76a -r 9b04bbfe3f90 web/request.py --- 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, diff -r eab61515d76a -r 9b04bbfe3f90 web/views/actions.py --- 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' diff -r eab61515d76a -r 9b04bbfe3f90 web/views/apacherewrite.py --- 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 : diff -r eab61515d76a -r 9b04bbfe3f90 web/views/basecomponents.py --- 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'') -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) diff -r eab61515d76a -r 9b04bbfe3f90 web/views/boxes.py --- 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"""
@@ -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'rss\n' % (html_escape(url), rss)) diff -r eab61515d76a -r 9b04bbfe3f90 web/views/facets.py --- 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') diff -r eab61515d76a -r 9b04bbfe3f90 web/views/magicsearch.py --- 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) diff -r eab61515d76a -r 9b04bbfe3f90 web/views/tableview.py --- 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 diff -r eab61515d76a -r 9b04bbfe3f90 web/views/urlpublishing.py --- 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.