backport stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 18 Feb 2010 07:29:13 +0100
changeset 4620 c4adfc2466f5
parent 4597 e872097f2287 (current diff)
parent 4619 f4254586e867 (diff)
child 4621 fe1c5a46a1cb
backport stable
--- a/cwconfig.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/cwconfig.py	Thu Feb 18 07:29:13 2010 +0100
@@ -328,7 +328,7 @@
         cubes = set()
         for directory in cls.cubes_search_path():
             if not os.path.exists(directory):
-                self.error('unexistant directory in cubes search path: %s'
+                cls.error('unexistant directory in cubes search path: %s'
                            % directory)
                 continue
             for cube in os.listdir(directory):
@@ -347,7 +347,7 @@
                     path.append(directory)
         except KeyError:
             pass
-        if not cls.CUBES_DIR in path:
+        if not cls.CUBES_DIR in path and exists(cls.CUBES_DIR):
             path.append(cls.CUBES_DIR)
         return path
 
@@ -474,7 +474,7 @@
         from logilab.common.modutils import load_module_from_file
         cls.cls_adjust_sys_path()
         for ctlfile in ('web/webctl.py',  'etwist/twctl.py',
-                        'server/serverctl.py', 
+                        'server/serverctl.py',
                         'devtools/devctl.py', 'goa/goactl.py'):
             if exists(join(CW_SOFTWARE_ROOT, ctlfile)):
                 try:
--- a/cwctl.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/cwctl.py	Thu Feb 18 07:29:13 2010 +0100
@@ -297,14 +297,18 @@
         # create the registry directory for this instance
         print '\n'+underline_title('Creating the instance %s' % appid)
         create_dir(config.apphome)
-        # load site_cubicweb from the cubes dir (if any)
-        config.load_site_cubicweb()
         # cubicweb-ctl configuration
         print '\n'+underline_title('Configuring the instance (%s.conf)' % configname)
         config.input_config('main', self.config.config_level)
         # configuration'specific stuff
         print
         helper.bootstrap(cubes, self.config.config_level)
+        # input for cubes specific options
+        for section in set(sect.lower() for sect, opt, optdict in config.all_options()
+                           if optdict.get('inputlevel') <= self.config.config_level):
+            if section not in ('main', 'email', 'pyro'):
+                print '\n' + underline_title('%s options' % section)
+                config.input_config(section, self.config.config_level)
         # write down configuration
         config.save()
         self._handle_win32(config, appid)
--- a/devtools/dataimport.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/devtools/dataimport.py	Thu Feb 18 07:29:13 2010 +0100
@@ -134,7 +134,7 @@
         try:
             for func in funcs:
                 res[dest] = func(res[dest])
-            if res[dest] is None or res[dest]==False:
+            if res[dest] is None:
                 raise AssertionError('undetermined value')
         except AssertionError, err:
             if optional in funcs:
@@ -220,7 +220,26 @@
     return txt.strip()
 
 def yesno(value):
-    return value.lower()[0] in 'yo1'
+    """simple heuristic that returns boolean value
+
+    >>> yesno("Yes")
+    True
+    >>> yesno("oui")
+    True
+    >>> yesno("1")
+    True
+    >>> yesno("11")
+    True
+    >>> yesno("")
+    False
+    >>> yesno("Non")
+    False
+    >>> yesno("blablabla")
+    False
+    """
+    if value:
+        return value.lower()[0] in 'yo1'
+    return False
 
 def isalpha(value):
     if value.isalpha():
--- a/misc/migration/postcreate.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/misc/migration/postcreate.py	Thu Feb 18 07:29:13 2010 +0100
@@ -5,7 +5,6 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
-
 # insert versions
 create_entity('CWProperty', pkey=u'system.version.cubicweb',
               value=unicode(config.cubicweb_version()))
@@ -38,9 +37,8 @@
         print 'you are using a manager account as anonymous user.'
         print 'Hopefully this is not a production instance...'
     elif anonlogin:
-        rql('INSERT CWUser X: X login %(login)s, X upassword %(pwd)s,'
-            'X in_group G WHERE G name "guests"',
-            {'login': unicode(anonlogin), 'pwd': anonpwd})
+        from cubicweb.server import create_user
+        create_user(session, unicode(anonlogin), anonpwd, 'guests')
 
 # need this since we already have at least one user in the database (the default admin)
 for user in rql('Any X WHERE X is CWUser').entities():
--- a/schema.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/schema.py	Thu Feb 18 07:29:13 2010 +0100
@@ -254,7 +254,9 @@
         return
     # if 'owners' in allowed groups, check if the user actually owns this
     # object, if so that's enough
-    if 'owners' in groups and 'eid' in kwargs and session.user.owns(kwargs['eid']):
+    if 'owners' in groups and (
+          kwargs.get('creating')
+          or ('eid' in kwargs and session.user.owns(kwargs['eid']))):
         return
     # else if there is some rql expressions, check them
     if any(rqlexpr.check(session, **kwargs)
@@ -787,20 +789,25 @@
 
         session may actually be a request as well
         """
-        if self.eid is not None:
+        creating = kwargs.get('creating')
+        if not creating and self.eid is not None:
             key = (self.eid, tuple(sorted(kwargs.iteritems())))
             try:
                 return session.local_perm_cache[key]
             except KeyError:
                 pass
         rql, has_perm_defs, keyarg = self.transform_has_permission()
+        if creating:
+            # when creating an entity, consider has_*_permission satisfied
+            if has_perm_defs:
+                return True
+            return False
         if keyarg is None:
             # on the server side, use unsafe_execute, but this is not available
             # on the client side (session is actually a request)
             execute = getattr(session, 'unsafe_execute', session.execute)
-            # XXX what if 'u' in kwargs
+            kwargs.setdefault('u', session.user.eid)
             cachekey = kwargs.keys()
-            kwargs['u'] = session.user.eid
             try:
                 rset = execute(rql, kwargs, cachekey, build_descr=True)
             except NotImplementedError:
@@ -864,12 +871,15 @@
             rql += ', U eid %(u)s'
         return rql
 
-    def check(self, session, eid=None):
+    def check(self, session, eid=None, creating=False, **kwargs):
         if 'X' in self.rqlst.defined_vars:
             if eid is None:
+                if creating:
+                    return self._check(session, creating=True, **kwargs)
                 return False
-            return self._check(session, x=eid)
-        return self._check(session)
+            assert creating == False
+            return self._check(session, x=eid, **kwargs)
+        return self._check(session, **kwargs)
 
 
 class RRQLExpression(RQLExpression):
--- a/server/__init__.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/server/__init__.py	Thu Feb 18 07:29:13 2010 +0100
@@ -93,6 +93,14 @@
 
 # database initialization ######################################################
 
+def create_user(session, login, pwd, *groups):
+    # monkey patch this method if you want to customize admin/anon creation
+    # (that maybe necessary if you change CWUser's schema)
+    session.create_entity('CWUser', login=login, upassword=pwd)
+    for group in groups:
+        session.execute('SET U in_group G WHERE G name %(group)s',
+                        {'group': group})
+
 def init_repository(config, interactive=True, drop=False, vreg=None):
     """initialise a repository database by creating tables add filling them
     with the minimal set of entities (ie at least the schema, base groups and
@@ -161,9 +169,7 @@
     for group in sorted(BASE_GROUPS):
         session.execute('INSERT CWGroup X: X name %(name)s',
                         {'name': unicode(group)})
-    session.execute('INSERT CWUser X: X login %(login)s, X upassword %(pwd)s',
-                    {'login': login, 'pwd': pwd})
-    session.execute('SET U in_group G WHERE G name "managers"')
+    create_user(session, login, pwd, 'managers')
     session.commit()
     # reloging using the admin user
     config._cubes = None # avoid assertion error
--- a/test/data/rrqlexpr_on_attr.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/test/data/rrqlexpr_on_attr.py	Thu Feb 18 07:29:13 2010 +0100
@@ -20,6 +20,5 @@
 class attr(RelationType):
     __permissions__ = {
         'read': ('managers', ),
-        'add': ('managers', RRQLExpression('S bla Y'),),
-        'delete': ('managers',),
+        'update': ('managers', RRQLExpression('S bla Y'),),
         }
--- a/test/unittest_schema.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/test/unittest_schema.py	Thu Feb 18 07:29:13 2010 +0100
@@ -257,7 +257,7 @@
         self.assertEquals(str(ex), msg)
 
     def test_rrqlexpr_on_etype(self):
-        self._test('rrqlexpr_on_eetype.py', "can't use RRQLExpression on an entity type, use an ERQLExpression (ToTo)")
+        self._test('rrqlexpr_on_eetype.py', "can't use RRQLExpression on ToTo, use an ERQLExpression")
 
     def test_erqlexpr_on_rtype(self):
         self._test('erqlexpr_on_ertype.py', "can't use ERQLExpression on relation ToTo toto TuTu, use a RRQLExpression")
--- a/web/data/cubicweb.login.css	Tue Feb 16 11:30:52 2010 +0100
+++ b/web/data/cubicweb.login.css	Thu Feb 18 07:29:13 2010 +0100
@@ -1,7 +1,7 @@
 /* styles for the login popup and login form
  *
  *  :organization: Logilab
- *  :copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+ *  :copyright: 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
  *  :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
  */
 
@@ -11,8 +11,11 @@
   right: 0px;
   width: 26em;
   padding: 0px 1px 1px;
+  background: #E4EAD8;
+}
+
+div#popupLoginBox label{
   font-weight: bold;
-  background: #E4EAD8;
 }
 
 div#popupLoginBox div#loginContent {
@@ -72,7 +75,7 @@
  }
 
 #loginContent input.data {
-  width:12em;
+  width: 12em;
 }
 
 .loginButton {
@@ -81,3 +84,7 @@
   margin: 2px 0px 0px;
   background: #f0eff0 url("gradient-grey-up.png") left top repeat-x;
 }
+
+#loginContent .formButtonBar {
+  float: right;
+}
--- a/web/facet.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/web/facet.py	Thu Feb 18 07:29:13 2010 +0100
@@ -268,25 +268,16 @@
     needs_update = False
     start_unfolded = True
 
-    def __init__(self, req, rset=None, rqlst=None, filtered_variable=None,
+    def __init__(self, req, rqlst=None, filtered_variable=None,
                  **kwargs):
-        super(AbstractFacet, self).__init__(req, rset=rset, **kwargs)
-        assert rset is not None or rqlst is not None
+        super(AbstractFacet, self).__init__(req, **kwargs)
+        assert rqlst is not None
         assert filtered_variable
-        # facet retreived using `object_by_id` from an ajax call
-        if rset is None:
-            self.init_from_form(rqlst=rqlst)
-        # facet retreived from `select` using the result set to filter
-        else:
-            self.init_from_rset()
+        # take care: facet may be retreived using `object_by_id` from an ajax call
+        # or from `select` using the result set to filter
+        self.rqlst = rqlst
         self.filtered_variable = filtered_variable
 
-    def init_from_rset(self):
-        self.rqlst = self.cw_rset.syntax_tree().children[0]
-
-    def init_from_form(self, rqlst):
-        self.rqlst = rqlst
-
     @property
     def operator(self):
         # OR between selected values by default
@@ -521,6 +512,9 @@
     def get_widget(self):
         """return the widget instance to use to display this facet"""
         values = set(value for _, value in self.vocabulary() if value is not None)
+        # Rset with entities (the facet is selected) but without values
+        if len(values) == 0:
+            return None
         return self.wdgclass(self, min(values), max(values))
 
     def infvalue(self):
--- a/web/uicfg.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/web/uicfg.py	Thu Feb 18 07:29:13 2010 +0100
@@ -379,7 +379,8 @@
                     continue
                 rdef = rschema.role_rdef(eschema, tschema, role)
                 if rschema.final:
-                    if not rdef.has_perm(cw, permission, eid=eid):
+                    if not rdef.has_perm(cw, permission, eid=eid,
+                                         creating=eid is None):
                         continue
                 elif strict or not rdef.has_local_role(relpermission):
                     if role == 'subject':
--- a/web/views/basetemplates.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/web/views/basetemplates.py	Thu Feb 18 07:29:13 2010 +0100
@@ -9,6 +9,7 @@
 __docformat__ = "restructuredtext en"
 
 from logilab.mtconverter import xml_escape
+from logilab.common.deprecation import class_renamed
 
 from cubicweb.appobject import objectify_selector
 from cubicweb.selectors import match_kwargs
@@ -398,7 +399,7 @@
         if state[0] == 'normal':
             return
         _ = self._cw._
-        value = self.view('oneline', self._cw.eid_rset(state[1][1]))
+        value = self._cw.view('oneline', self._cw.eid_rset(state[1][1]))
         msg = ' '.join((_("searching for"),
                         display_name(self._cw, state[1][3]),
                         _("to associate with"), value,
@@ -465,12 +466,13 @@
 class LogForm(forms.FieldsForm):
     __regid__ = 'logform'
     domid = 'loginForm'
+    needs_css = ('cubicweb.login.css',)
     # XXX have to recall fields name since python is mangling __login/__password
     __login = ff.StringField('__login', widget=fw.TextInput({'class': 'data'}))
     __password = ff.StringField('__password', label=_('password'),
                                 widget=fw.PasswordSingleInput({'class': 'data'}))
     form_buttons = [fw.SubmitButton(label=_('log in'),
-                                    attrs={'class': 'loginButton right'})]
+                                    attrs={'class': 'loginButton'})]
 
     @property
     def action(self):
@@ -484,7 +486,6 @@
     title = 'log in'
 
     def call(self, id, klass, title=True, showmessage=True):
-        self._cw.add_css('cubicweb.login.css')
         self.w(u'<div id="%s" class="%s">' % (id, klass))
         if title:
             stitle = self._cw.property_value('ui.site-title')
@@ -512,6 +513,8 @@
         self.w(form.render(table_class='', display_progress_div=False))
         cw.html_headers.add_onload('jQuery("#__login:visible").focus()')
 
+LogFormTemplate = class_renamed('LogFormTemplate', LogFormView)
+
 def login_form_url(req):
     if req.https:
         return req.url()
--- a/web/views/facets.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/web/views/facets.py	Thu Feb 18 07:29:13 2010 +0100
@@ -69,37 +69,38 @@
         rset, vid, divid, paginate = self._get_context(view)
         if rset.rowcount < 2: # XXX done by selectors, though maybe necessary when rset has been hijacked
             return
+        rqlst = self.cw_rset.syntax_tree()
+        # union not yet supported
+        if len(rqlst.children) != 1:
+            return ()
+        rqlst = rqlst.copy()
+        req.vreg.rqlhelper.annotate(rqlst)
+        mainvar, baserql = prepare_facets_rqlst(rqlst, rset.args)
+        widgets = []
+        for facet in self.get_facets(rset, rqlst.children[0], mainvar):
+            if facet.cw_propval('visible'):
+                wdg = facet.get_widget()
+                if wdg is not None:
+                    widgets.append(wdg)
+        if not widgets:
+            return
         if vid is None:
             vid = req.form.get('vid')
-        rqlst = rset.syntax_tree()
-        rqlst.save_state()
-        try:
-            mainvar, baserql = prepare_facets_rqlst(rqlst, rset.args)
-            widgets = []
-            for facet in self.get_facets(rset, mainvar):
-                if facet.cw_propval('visible'):
-                    wdg = facet.get_widget()
-                    if wdg is not None:
-                        widgets.append(wdg)
-            if not widgets:
-                return
-            if self.bk_linkbox_template:
-                self.display_bookmark_link(rset)
-            w = self.w
-            w(u'<form method="post" id="%sForm" cubicweb:facetargs="%s" action="">'  % (
-                divid, xml_escape(dumps([divid, vid, paginate, self.facetargs()]))))
-            w(u'<fieldset>')
-            hiddens = {'facets': ','.join(wdg.facet.__regid__ for wdg in widgets),
-                       'baserql': baserql}
-            for param in ('subvid', 'vtitle'):
-                if param in req.form:
-                    hiddens[param] = req.form[param]
-            filter_hiddens(w, **hiddens)
-            for wdg in widgets:
-                wdg.render(w=self.w)
-            w(u'</fieldset>\n</form>\n')
-        finally:
-            rqlst.recover()
+        if self.bk_linkbox_template:
+            self.display_bookmark_link(rset)
+        w = self.w
+        w(u'<form method="post" id="%sForm" cubicweb:facetargs="%s" action="">'  % (
+            divid, xml_escape(dumps([divid, vid, paginate, self.facetargs()]))))
+        w(u'<fieldset>')
+        hiddens = {'facets': ','.join(wdg.facet.__regid__ for wdg in widgets),
+                   'baserql': baserql}
+        for param in ('subvid', 'vtitle'):
+            if param in req.form:
+                hiddens[param] = req.form[param]
+        filter_hiddens(w, **hiddens)
+        for wdg in widgets:
+            wdg.render(w=self.w)
+        w(u'</fieldset>\n</form>\n')
 
     def display_bookmark_link(self, rset):
         eschema = self._cw.vreg.schema.eschema('Bookmark')
@@ -115,10 +116,10 @@
                     self._cw._('bookmark this search'))
             self.w(self.bk_linkbox_template % bk_link)
 
-    def get_facets(self, rset, mainvar):
-        return self._cw.vreg['facets'].poss_visible_objects(self._cw, rset=rset,
-                                                        context='facetbox',
-                                                        filtered_variable=mainvar)
+    def get_facets(self, rset, rqlst, mainvar):
+        return self._cw.vreg['facets'].poss_visible_objects(
+            self._cw, rset=rset, rqlst=rqlst,
+            context='facetbox', filtered_variable=mainvar)
 
 # facets ######################################################################
 
--- a/web/views/tableview.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/web/views/tableview.py	Thu Feb 18 07:29:13 2010 +0100
@@ -33,13 +33,13 @@
         # union not yet supported
         if len(rqlst.children) != 1:
             return ()
-        rqlst.save_state()
+        rqlst = rqlst.copy()
+        self._cw.vreg.rqlhelper.annotate(rqlst)
         mainvar, baserql = prepare_facets_rqlst(rqlst, self.cw_rset.args)
         wdgs = [facet.get_widget() for facet in self._cw.vreg['facets'].poss_visible_objects(
-            self._cw, rset=self.cw_rset, context='tablefilter',
+            self._cw, rset=self.cw_rset, rqlst=rqlst.children[0], context='tablefilter',
             filtered_variable=mainvar)]
         wdgs = [wdg for wdg in wdgs if wdg is not None]
-        rqlst.recover()
         if wdgs:
             self._generate_form(divid, baserql, wdgs, hidden,
                                vidargs={'displaycols': displaycols,
--- a/web/views/tabs.py	Tue Feb 16 11:30:52 2010 +0100
+++ b/web/views/tabs.py	Thu Feb 18 07:29:13 2010 +0100
@@ -8,6 +8,7 @@
 
 __docformat__ = "restructuredtext en"
 
+from logilab.common.deprecation import class_renamed
 from logilab.mtconverter import xml_escape
 
 from cubicweb import NoSelectableObject, role
@@ -187,11 +188,11 @@
 
     def cell_call(self, row, col):
         entity = self.cw_rset.complete_entity(row, col)
+        self.render_entity_toolbox(entity)
         self.render_entity_title(entity)
-        # XXX uncomment this in 3.6
-        #self.render_entity_toolbox(entity)
         self.render_tabs(self.tabs, self.default_tab, entity)
-TabedPrimaryView = TabbedPrimaryView # XXX deprecate that typo!
+
+TabedPrimaryView = class_renamed('TabedPrimaryView', TabbedPrimaryView)
 
 class PrimaryTab(primary.PrimaryView):
     __regid__ = 'main_tab'