--- a/common/selectors.py Mon Feb 16 19:20:30 2009 +0100
+++ b/common/selectors.py Mon Feb 16 19:50:10 2009 +0100
@@ -70,7 +70,8 @@
oid = cls.id
ret = selector(cls, *args, **kwargs)
if TRACED_OIDS == 'all' or oid in TRACED_OIDS:
- SELECTOR_LOGGER.warning('selector %s returned %s for %s', selname, ret, cls)
+ #SELECTOR_LOGGER.warning('selector %s returned %s for %s', selname, ret, cls)
+ print 'selector %s returned %s for %s' % (selname, ret, cls)
return ret
traced.__name__ = selector.__name__
return traced
@@ -213,9 +214,10 @@
class match_search_state(Selector):
- def __init__(self, *expected_states):
- self.expected_states = expected_states
+ def __init__(self, *expected):
+ self.expected = expected
+ @lltrace
def __call__(self, cls, req, rset, row=None, col=0, **kwargs):
"""checks if the current request search state is in one of the expected states
the wrapped class
@@ -224,12 +226,40 @@
object to create a relation with another)
"""
try:
- if not req.search_state[0] in cls.search_states:
+ if not req.search_state[0] in self.expected:
return 0
except AttributeError:
return 1 # class doesn't care about search state, accept it
return 1
+
+class match_form_params(match_search_state):
+ """check if parameters specified as initializer arguments are specified
+ in request form parameters
+ """
+ @lltrace
+ def __call__(self, cls, req, *args, **kwargs):
+ score = 0
+ for param in self.expected:
+ val = req.form.get(param)
+ if not val:
+ return 0
+ score += 1
+ return len(self.expected)
+
+
+class match_kwargs(match_search_state):
+ """check if parameters specified as initializer arguments are specified
+ in named parameters
+ """
+ @lltrace
+ def __call__(self, cls, req, *args, **kwargs):
+ for arg in self.expected:
+ if not arg in kwargs:
+ return 0
+ return len(self.expected)
+
+
@lltrace
def anonymous_user(cls, req, *args, **kwargs):
"""accept if user is anonymous"""
@@ -244,32 +274,6 @@
return not anonymous_user(cls, req, *args, **kwargs)
not_anonymous_selector = deprecated_function(authenticated_user)
-@lltrace
-def match_form_params(cls, req, *args, **kwargs):
- """check if parameters specified by the form_params attribute on
- the wrapped class are specified in request form parameters
- """
- score = 0
- for param in cls.form_params:
- val = req.form.get(param)
- if not val:
- return 0
- score += 1
- return score + 1
-req_form_params_selector = deprecated_function(match_form_params)
-
-@lltrace
-def match_kwargs(cls, req, *args, **kwargs):
- """check if arguments specified by the expected_kwargs attribute on
- the wrapped class are specified in given named parameters
- """
- values = []
- for arg in cls.expected_kwargs:
- if not arg in kwargs:
- return 0
- return 1
-kwargs_selector = deprecated_function(match_kwargs)
-
# abstract selectors ##########################################################
class EClassSelector(Selector):
@@ -364,19 +368,39 @@
if implements_iface(eclass, iface):
score += 1
if getattr(iface, '__registry__', None) == 'etypes':
+ score += 1
# adjust score if the interface is an entity class
if iface is eclass:
- score += len(eclass.e_schema.ancestors()) + 1
+ score += len(eclass.e_schema.ancestors())
+ print 'is majoration', len(eclass.e_schema.ancestors())
else:
parents = [e.type for e in eclass.e_schema.ancestors()]
for index, etype in enumerate(reversed(parents)):
basecls = eclass.vreg.etype_class(etype)
if iface is basecls:
- score += index + 1
+ score += index
+ print 'etype majoration', index
break
return score
+class specified_etype_implements(implements):
+ """return the "interface score" for class associated to 'etype' (expected in
+ request form or arguments)
+ """
+
+ @lltrace
+ def __call__(cls, req, *args, **kwargs):
+ try:
+ etype = req.form['etype']
+ except KeyError:
+ try:
+ etype = kwargs['etype']
+ except KeyError:
+ return 0
+ return self.score_class(cls.vreg.etype_class(etype), req)
+
+
class relation_possible(EClassSelector):
"""initializer takes relation name as argument and an optional role (default
as subject) and target type (default to unspecified)
@@ -490,8 +514,8 @@
class may_add_relation(EntitySelector):
- """initializer a relation type and optional role (default to 'subject') as
- argument
+ """initializer takes a relation type and optional role (default to
+ 'subject') as argument
if row is specified check the relation may be added to the entity at the
given row/col (if row specified) or to every entities in the given col (if
@@ -565,7 +589,9 @@
class has_add_permission(EClassSelector):
-
+ """return 1 if the user may add some entity of the types found in the
+ result set (0 else)
+ """
def score_class(self, eclass, req):
eschema = eclass.e_schema
if not (eschema.is_final() or eschema.is_subobject(strict=True)) \
@@ -575,25 +601,20 @@
class score_entity(EntitySelector):
+ """initializer takes a function as argument (which is expected to take an
+ entity as argument)
+
+ return the score returned by the function on the entity at the given row/col
+ (if row specified) or the sum of the score for every entities in the given
+ col (if row is not specified). Return 0 at the first entity scoring to zero.
+ """
def __init__(self, scorefunc):
self.score_entity = scorefunc
+
# XXX not so basic selectors ######################################################
@lltrace
-def accept_etype(cls, req, *args, **kwargs):
- """check etype presence in request form *and* accepts conformance"""
- try:
- etype = req.form['etype']
- except KeyError:
- try:
- etype = kwargs['etype']
- except KeyError:
- return 0
- return implements(*cls.accepts).score_class(cls.vreg.etype_class(etype), req)
-etype_form_selector = deprecated_function(accept_etype)
-
-@lltrace
def _rql_condition(cls, req, rset, row=None, col=0, **kwargs):
"""accept single entity result set if the entity match an rql condition
"""
@@ -697,14 +718,20 @@
return implements(*cls.accepts_interfaces)(cls, req, rset, row, col)
_interface_selector = deprecated_function(implement_interface)
interface_selector = deprecated_function(implement_interface)
-implement_interface = deprecated_function(implement_interface)
+implement_interface = deprecated_function(implement_interface, 'use implements')
+
+def accept_etype(cls, req, *args, **kwargs):
+ """check etype presence in request form *and* accepts conformance"""
+ return specified_etype_implements(*cls.accepts)(cls, req, *args)
+etype_form_selector = deprecated_function(accept_etype)
+accept_etype = deprecated_function(accept_etype, 'use specified_etype_implements')
def searchstate_selector(cls, req, rset, row=None, col=0, **kwargs):
return match_search_state(cls.search_states)(cls, req, rset, row, col)
searchstate_selector = deprecated_function(searchstate_selector)
def match_user_group(cls, req, rset=None, row=None, col=0, **kwargs):
- return match_user_groups(cls.require_groups)(cls, req, rset, row, col, **kwargs)
+ return match_user_groups(*cls.require_groups)(cls, req, rset, row, col, **kwargs)
in_group_selector = deprecated_function(match_user_group)
match_user_group = deprecated_function(match_user_group)
@@ -753,3 +780,6 @@
name='searchstate_accept_one_but_etype')
searchstate_accept_one_but_etype_selector = deprecated_function(
searchstate_accept_one_but_etype)
+
+#req_form_params_selector = deprecated_function(match_form_params) # form_params
+#kwargs_selector = deprecated_function(match_kwargs) # expected_kwargs
--- a/common/view.py Mon Feb 16 19:20:30 2009 +0100
+++ b/common/view.py Mon Feb 16 19:50:10 2009 +0100
@@ -8,18 +8,41 @@
__docformat__ = "restructuredtext en"
from cStringIO import StringIO
+from warnings import warn
from logilab.mtconverter import html_escape
from cubicweb import NotAnEntity, NoSelectableObject
from cubicweb.common.registerers import accepts_registerer, priority_registerer
-from cubicweb.common.selectors import (chainfirst, match_user_group, accept,
- nonempty_rset, empty_rset, none_rset)
+from cubicweb.common.selectors import (yes, match_user_groups, implements,
+ nonempty_rset, none_rset)
from cubicweb.common.appobject import AppRsetObject, ComponentMixIn
from cubicweb.common.utils import UStringIO, HTMLStream
_ = unicode
+
+def require_group_compat(registered):
+ def plug_selector(cls, vreg):
+ cls = registered(cls, vreg)
+ if getattr(cls, 'require_groups', None):
+ warn('use "use match_user_groups(group1, group2)" instead of using require_groups',
+ DeprecationWarning)
+ cls.__selectors__ += (match_user_groups(cls.require_groups),)
+ return cls
+ return classmethod(plug_selector)
+
+def accepts_compat(registered):
+ def plug_selector(cls, vreg):
+ cls = registered(cls, vreg)
+ if getattr(cls, 'accepts', None):
+ warn('use "use match_user_groups(group1, group2)" instead of using require_groups',
+ DeprecationWarning)
+ cls.__selectors__ += (implements(*cls.accepts),)
+ return cls
+ return classmethod(plug_selector)
+
+
# robots control
NOINDEX = u'<meta name="ROBOTS" content="NOINDEX" />'
NOFOLLOW = u'<meta name="ROBOTS" content="NOFOLLOW" />'
@@ -302,9 +325,11 @@
"""base class for views applying on an entity (i.e. uniform result set)
"""
__registerer__ = accepts_registerer
- __selectors__ = (accept,)
+ __selectors__ = (implements('Any'),)
+ registered = accepts_compat(View.registered.im_func)
+
category = 'entityview'
-
+
def field(self, label, value, row=True, show_label=True, w=None, tr=True):
""" read-only field """
if w is None:
@@ -325,10 +350,11 @@
to be displayed (so they can always be displayed !)
"""
__registerer__ = priority_registerer
- __selectors__ = (match_user_group, none_rset)
- require_groups = ()
+ __selectors__ = (none_rset,)
+ registered = require_group_compat(View.registered.im_func)
+
category = 'startupview'
-
+
def url(self):
"""return the url associated with this view. We can omit rql here"""
return self.build_url('view', vid=self.id)
@@ -347,7 +373,7 @@
result set (usually a default rql is provided by the view class)
"""
__registerer__ = accepts_registerer
- __selectors__ = (chainfirst(none_rset, accept),)
+ __selectors__ = ((none_rset | implements('Any')),)
default_rql = None
@@ -404,7 +430,7 @@
labels.append(label)
return labels
-
+
# concrete template base classes ##############################################
class Template(View):
@@ -413,9 +439,9 @@
"""
__registry__ = 'templates'
__registerer__ = priority_registerer
- __selectors__ = (match_user_group,)
+ __selectors__ = (yes,)
- require_groups = ()
+ registered = require_group_compat(View.registered.im_func)
def template(self, oid, **kwargs):
"""shortcut to self.registry.render method on the templates registry"""
--- a/sobjects/notification.py Mon Feb 16 19:20:30 2009 +0100
+++ b/sobjects/notification.py Mon Feb 16 19:50:10 2009 +0100
@@ -1,7 +1,7 @@
"""some hooks and views to handle notification on entity's changes
: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"
@@ -21,7 +21,7 @@
from cubicweb.common.view import EntityView
from cubicweb.common.appobject import Component
from cubicweb.common.registerers import accepts_registerer
-from cubicweb.common.selectors import accept
+from cubicweb.common.selectors import implements
from cubicweb.common.mail import format_mail
from cubicweb.server.pool import PreCommitOperation
@@ -38,8 +38,7 @@
"""
id = 'recipients_finder'
__registerer__ = accepts_registerer
- __selectors__ = (accept,)
- accepts = ('Any',)
+ __selectors__ = (implements('Any'),)
user_rql = ('Any X,E,A WHERE X is EUser, X in_state S, S name "activated",'
'X primary_email E, E address A')
@@ -299,7 +298,7 @@
class CardAddedView(NormalizedTextView):
"""get notified from new cards"""
- accepts = ('Card',)
+ __selectors__ = (implements('Card'),)
content_attr = 'synopsis'
--- a/test/unittest_rset.py Mon Feb 16 19:20:30 2009 +0100
+++ b/test/unittest_rset.py Mon Feb 16 19:50:10 2009 +0100
@@ -227,7 +227,8 @@
self.assertEquals(e.col, 0)
self.assertEquals(e['title'], 'zou')
self.assertRaises(KeyError, e.__getitem__, 'path')
- self.assertEquals(e.view('text'), 'zou')
+ with traced_selection():
+ self.assertEquals(e.view('text'), 'zou')
self.assertEquals(pprelcachedict(e._related_cache), [])
e = rset.get_entity(0, 1)
--- a/test/unittest_vregistry.py Mon Feb 16 19:20:30 2009 +0100
+++ b/test/unittest_vregistry.py Mon Feb 16 19:50:10 2009 +0100
@@ -22,7 +22,7 @@
self.vreg.load_file(join(BASE, 'web', 'views'), 'euser.py')
self.vreg.load_file(join(BASE, 'web', 'views'), 'baseviews.py')
fpvc = [v for v in self.vreg.registry_objects('views', 'primary')
- i f v.__module__ == 'cubicweb.web.views.euser'][0]
+ if v.__module__ == 'cubicweb.web.views.euser'][0]
fpv = fpvc(None, None)
# don't want a TypeError due to super call
self.assertRaises(AttributeError, fpv.render_entity_attributes, None, None)
--- a/web/views/baseforms.py Mon Feb 16 19:20:30 2009 +0100
+++ b/web/views/baseforms.py Mon Feb 16 19:50:10 2009 +0100
@@ -17,9 +17,9 @@
from cubicweb.interfaces import IWorkflowable
from cubicweb.common.utils import make_uid
from cubicweb.common.uilib import cut
-from cubicweb.common.selectors import (accept_etype, match_kwargs,
- one_line_rset, implement_interface,
- match_form_params, accept)
+from cubicweb.common.selectors import (specified_etype_implements,
+ match_kwargs, match_form_params,
+ one_line_rset, implements)
from cubicweb.common.view import EntityView
from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param
from cubicweb.web.controller import NAV_FORM_PARAMETERS
@@ -90,9 +90,7 @@
id = 'statuschange'
title = _('status change')
- __selectors__ = (implement_interface, match_form_params)
- accepts_interfaces = (IWorkflowable,)
- form_params = ('treid',)
+ __selectors__ = (implements(IWorkflowable), match_form_params('treid'))
def cell_call(self, row, col, vid='secondary'):
entity = self.entity(row, col)
@@ -153,8 +151,7 @@
class ClickAndEditForm(EntityForm):
id = 'reledit'
- __selectors__ = (match_kwargs, )
- expected_kwargs = ('rtype',)
+ __selectors__ = (match_kwargs('rtype'), )
#FIXME editableField class could be toggleable from userprefs
@@ -219,7 +216,7 @@
dynamic default values such as the 'tomorrow' date or the user's login
being connected
"""
- __selectors__ = (one_line_rset, accept)
+ __selectors__ = (one_line_rset, implements('Any'))
id = 'edition'
title = _('edition')
@@ -526,7 +523,7 @@
class CreationForm(EditionForm):
- __selectors__ = (accept_etype, )
+ __selectors__ = (specified_etype_implements('Any'), )
id = 'creation'
title = _('creation')
@@ -639,8 +636,8 @@
class InlineEntityCreationForm(InlineFormMixIn, CreationForm):
id = 'inline-creation'
- __selectors__ = (match_kwargs, accept_etype)
- expected_kwargs = ('ptype', 'peid', 'rtype')
+ __selectors__ = (match_kwargs('ptype', 'peid', 'rtype'), specified_etype_implements('Any'))
+
EDITION_BODY = u'''\
<div id="div-%(parenteid)s-%(rtype)s-%(eid)s" class="inlinedform">
@@ -678,8 +675,7 @@
class InlineEntityEditionForm(InlineFormMixIn, EditionForm):
id = 'inline-edition'
- __selectors__ = (accept, match_kwargs)
- expected_kwargs = ('ptype', 'peid', 'rtype')
+ __selectors__ = (implements('Any'), match_kwargs('ptype', 'peid', 'rtype'))
EDITION_BODY = u'''\
<div onclick="restoreInlinedEntity('%(parenteid)s', '%(rtype)s', '%(eid)s')" id="div-%(parenteid)s-%(rtype)s-%(eid)s" class="inlinedform">
@@ -881,8 +877,7 @@
class UnrelatedDivs(EntityView):
id = 'unrelateddivs'
- __selectors__ = (match_form_params,)
- form_params = ('relation',)
+ __selectors__ = (match_form_params('relation',),)
@property
def limit(self):
@@ -993,7 +988,6 @@
THIS IS A TEXT VIEW. DO NOT HTML_ESCAPE
"""
id = 'combobox'
- accepts = ('Any',)
title = None
def cell_call(self, row, col):