--- a/.hgtags Wed May 27 08:39:16 2009 +0200
+++ b/.hgtags Wed May 27 13:01:32 2009 +0200
@@ -28,3 +28,11 @@
0e132fbae9cc5e004f4b79a8b842addad43519a7 cubicweb-debian-version-3_1_4-1
c14231e3a4f9120e2bb6a1d8690252fff5e48131 cubicweb-version-3_2_0
c9c492787a8aa1b7916e22eb6498cba1c8fa316c cubicweb-debian-version-3_2_0-1
+634c251dd032894850080c4e5aeb0a4e09f888c0 cubicweb-version-3_2_1
+e784f8847a124a93e5b385d7a92a2772c050fe82 cubicweb-debian-version-3_2_1-1
+6539ce84f04357ef65ccee0896a30997b16a4ece cubicweb-version-3_2_2
+92d1a15f08f7c5fa87643ffb4273d12cb3f41c63 cubicweb-debian-version-3_2_2-1
+6539ce84f04357ef65ccee0896a30997b16a4ece cubicweb-version-3_2_2
+9b21e068fef73c37bcb4e53d006a7bde485f390b cubicweb-version-3_2_2
+92d1a15f08f7c5fa87643ffb4273d12cb3f41c63 cubicweb-debian-version-3_2_2-1
+0e07514264aa1b0b671226f41725ea4c066c210a cubicweb-debian-version-3_2_2-1
--- a/MANIFEST.in Wed May 27 08:39:16 2009 +0200
+++ b/MANIFEST.in Wed May 27 13:01:32 2009 +0200
@@ -3,7 +3,7 @@
include bin/cubicweb-*
include man/cubicweb-ctl.1
-recursive-include doc *.txt *.zargo *.png *.html makefile
+recursive-include doc *.txt *.zargo *.png *.html makefile *.rst
recursive-include misc *
--- a/__init__.py Wed May 27 08:39:16 2009 +0200
+++ b/__init__.py Wed May 27 13:01:32 2009 +0200
@@ -46,7 +46,7 @@
class Binary(StringIO):
"""customize StringIO to make sure we don't use unicode"""
- def __init__(self, buf= ''):
+ def __init__(self, buf=''):
assert isinstance(buf, (str, buffer)), \
"Binary objects must use raw strings, not %s" % buf.__class__
StringIO.__init__(self, buf)
--- a/__pkginfo__.py Wed May 27 08:39:16 2009 +0200
+++ b/__pkginfo__.py Wed May 27 13:01:32 2009 +0200
@@ -6,7 +6,7 @@
distname = "cubicweb"
modname = "cubicweb"
-numversion = (3, 2, 0)
+numversion = (3, 2, 2)
version = '.'.join(str(num) for num in numversion)
license = 'LGPL v2'
--- a/cwconfig.py Wed May 27 08:39:16 2009 +0200
+++ b/cwconfig.py Wed May 27 13:01:32 2009 +0200
@@ -11,6 +11,7 @@
"""
__docformat__ = "restructuredtext en"
+_ = unicode
import sys
import os
@@ -27,7 +28,6 @@
CONFIGURATIONS = []
-_ = unicode
class metaconfiguration(type):
"""metaclass to automaticaly register configuration"""
--- a/cwvreg.py Wed May 27 08:39:16 2009 +0200
+++ b/cwvreg.py Wed May 27 13:01:32 2009 +0200
@@ -11,7 +11,7 @@
from rql import RQLHelper
-from cubicweb import Binary, UnknownProperty, UnknownEid
+from cubicweb import ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid
from cubicweb.vregistry import VRegistry, ObjectNotFound, NoSelectableObject
from cubicweb.rtags import RTAGS
@@ -170,7 +170,10 @@
# browse ancestors from most specific to most generic and
# try to find an associated custom entity class
for baseschema in baseschemas:
- btype = str(baseschema)
+ try:
+ btype = ETYPE_NAME_MAP[baseschema]
+ except KeyError:
+ btype = str(baseschema)
try:
cls = self.select(self.registry_objects('etypes', btype), etype)
break
@@ -372,6 +375,8 @@
default to a dump of the class registered for 'Any'
"""
usercls = super(MulCnxCubicWebRegistry, self).etype_class(etype)
+ if etype == 'Any':
+ return usercls
usercls.e_schema = self.schema.eschema(etype)
return usercls
@@ -385,7 +390,14 @@
vobject.vreg = self
vobject.schema = self.schema
vobject.config = self.config
- return super(MulCnxCubicWebRegistry, self).select(vobjects, *args, **kwargs)
+ selected = super(MulCnxCubicWebRegistry, self).select(vobjects, *args,
+ **kwargs)
+ # redo the same thing on the instance so it won't use equivalent class
+ # attributes (which may change)
+ selected.vreg = self
+ selected.schema = self.schema
+ selected.config = self.config
+ return selected
from datetime import datetime, date, time, timedelta
--- a/dbapi.py Wed May 27 08:39:16 2009 +0200
+++ b/dbapi.py Wed May 27 13:01:32 2009 +0200
@@ -13,7 +13,8 @@
from logging import getLogger
from time import time, clock
-from cubicweb import ConnectionError, RequestSessionMixIn, set_log_methods
+from logilab.common.logging_ext import set_log_methods
+from cubicweb import ETYPE_NAME_MAP, ConnectionError, RequestSessionMixIn
from cubicweb.cwvreg import CubicWebRegistry, MulCnxCubicWebRegistry
from cubicweb.cwconfig import CubicWebNoAppConfiguration
@@ -101,7 +102,12 @@
vreg = MulCnxCubicWebRegistry(config, initlog=initlog)
else:
vreg = CubicWebRegistry(config, initlog=initlog)
- vreg.set_schema(repo.get_schema())
+ schema = repo.get_schema()
+ for oldetype, newetype in ETYPE_NAME_MAP.items():
+ if oldetype in schema:
+ print 'aliasing', newetype, 'to', oldetype
+ schema._entities[newetype] = schema._entities[oldetype]
+ vreg.set_schema(schema)
else:
vreg = None
cnx = repo_connect(repo, user, password, cnxprops)
@@ -325,11 +331,6 @@
self.vreg = None
# session's data
self.data = {}
- # XXX < 3.2 bw compat
- if 'EUser' in self._repo.get_schema():
- self._user_etype = 'EUser'
- else:
- self._user_etype = 'CWUser'
def __repr__(self):
if self.anonymous_connection:
@@ -435,9 +436,9 @@
eid, login, groups, properties = self._repo.user_info(self.sessionid, props)
if req is None:
req = self.request()
- rset = req.eid_rset(eid, self._user_etype)
- user = self.vreg.etype_class(self._user_etype)(req, rset, row=0, groups=groups,
- properties=properties)
+ rset = req.eid_rset(eid, 'CWUser')
+ user = self.vreg.etype_class('CWUser')(req, rset, row=0, groups=groups,
+ properties=properties)
user['login'] = login # cache login
return user
--- a/debian/changelog Wed May 27 08:39:16 2009 +0200
+++ b/debian/changelog Wed May 27 13:01:32 2009 +0200
@@ -1,3 +1,15 @@
+cubicweb (3.2.2-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Nicolas Chauvat <nicolas.chauvat@logilab.fr> Wed, 27 May 2009 12:31:49 +0200
+
+cubicweb (3.2.1-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Aurélien Campéas <aurelien.campeas@logilab.fr> Mon, 25 May 2009 16:45:00 +0200
+
cubicweb (3.2.0-1) unstable; urgency=low
* new upstream release
--- a/devtools/htmlparser.py Wed May 27 08:39:16 2009 +0200
+++ b/devtools/htmlparser.py Wed May 27 13:01:32 2009 +0200
@@ -88,13 +88,13 @@
self.input_tags = self.find_tag('input')
self.title_tags = [self.h1_tags, self.h2_tags, self.h3_tags, self.h4_tags]
- def find_tag(self, tag):
+ def find_tag(self, tag, gettext=True):
"""return a list which contains text of all "tag" elements """
if self.default_ns is None:
iterstr = ".//%s" % tag
else:
iterstr = ".//{%s}%s" % (self.default_ns, tag)
- if tag in ('a', 'input'):
+ if not gettext or tag in ('a', 'input'):
return [(elt.text, elt.attrib) for elt in self.etree.iterfind(iterstr)]
return [u''.join(elt.xpath('.//text()')) for elt in self.etree.iterfind(iterstr)]
--- a/misc/migration/bootstrapmigration_repository.py Wed May 27 08:39:16 2009 +0200
+++ b/misc/migration/bootstrapmigration_repository.py Wed May 27 13:01:32 2009 +0200
@@ -7,6 +7,15 @@
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
+if applcubicwebversion < (3, 2, 2) and cubicwebversion >= (3, 2, 1):
+ from base64 import b64encode
+ for table in ('entities', 'deleted_entities'):
+ for eid, extid in sql('SELECT eid, extid FROM %s WHERE extid is NOT NULL'
+ % table, ask_confirm=False):
+ sql('UPDATE %s SET extid=%%(extid)s WHERE eid=%%(eid)s' % table,
+ {'extid': b64encode(extid), 'eid': eid}, ask_confirm=False)
+ checkpoint()
+
if applcubicwebversion < (3, 2, 0) and cubicwebversion >= (3, 2, 0):
add_cube('card', update_database=False)
@@ -24,7 +33,7 @@
add_relation_type('is_instance_of')
# fill the relation using an efficient sql query instead of using rql
sql('INSERT INTO is_instance_of_relation '
- ' SELECT * from is_relation')
+ ' SELECT * from is_relation')
checkpoint()
session.set_shared_data('do-not-insert-is_instance_of', False)
--- a/rset.py Wed May 27 08:39:16 2009 +0200
+++ b/rset.py Wed May 27 13:01:32 2009 +0200
@@ -380,9 +380,6 @@
pass
# build entity instance
etype = self.description[row][col]
- if etype == 'EUser':
- import traceback
- traceback.printstack()
entity = self.vreg.etype_class(etype)(req, self, row, col)
entity.set_eid(eid)
# cache entity
--- a/schemaviewer.py Wed May 27 08:39:16 2009 +0200
+++ b/schemaviewer.py Wed May 27 13:01:32 2009 +0200
@@ -93,10 +93,10 @@
return data
def eschema_link_url(self, eschema):
- return self.req.build_url('eetype/%s?vid=eschema' % eschema)
+ return self.req.build_url('cwetype/%s?vid=eschema' % eschema)
def rschema_link_url(self, rschema):
- return self.req.build_url('ertype/%s?vid=eschema' % rschema)
+ return self.req.build_url('cwrtype/%s?vid=eschema' % rschema)
def possible_views(self, etype):
rset = self.req.etype_rset(etype)
--- a/selectors.py Wed May 27 08:39:16 2009 +0200
+++ b/selectors.py Wed May 27 13:01:32 2009 +0200
@@ -114,7 +114,7 @@
def score_interface(cls_or_inst, cls, iface):
- """Return true if the give object (maybe an instance or class) implements
+ """Return XXX if the give object (maybe an instance or class) implements
the interface.
"""
if getattr(iface, '__registry__', None) == 'etypes':
--- a/server/querier.py Wed May 27 08:39:16 2009 +0200
+++ b/server/querier.py Wed May 27 13:01:32 2009 +0200
@@ -599,7 +599,8 @@
except KeyError:
raise QueryError('bad cache key %s (no value)' % key)
except TypeError:
- raise QueryError('bad cache key %s (value: %r)' % (key, args[key]))
+ raise QueryError('bad cache key %s (value: %r)' % (
+ key, args[key]))
except UnknownEid:
# we want queries such as "Any X WHERE X eid 9999"
# return an empty result instead of raising UnknownEid
--- a/server/repository.py Wed May 27 08:39:16 2009 +0200
+++ b/server/repository.py Wed May 27 13:01:32 2009 +0200
@@ -636,9 +636,10 @@
self.exception('unexpected error')
raise
- def close(self, sessionid):
+ def close(self, sessionid, checkshuttingdown=True):
"""close the session with the given id"""
- session = self._get_session(sessionid, setpool=True)
+ session = self._get_session(sessionid, setpool=True,
+ checkshuttingdown=checkshuttingdown)
# operation uncommited before close are rollbacked before hook is called
session.rollback()
self.hm.call_hooks('session_close', session=session)
@@ -691,7 +692,7 @@
"""close every opened sessions"""
for sessionid in self._sessions.keys():
try:
- self.close(sessionid)
+ self.close(sessionid, checkshuttingdown=False)
except:
self.exception('error while closing session %s' % sessionid)
@@ -720,9 +721,9 @@
session.set_pool()
return session
- def _get_session(self, sessionid, setpool=False):
+ def _get_session(self, sessionid, setpool=False, checkshuttingdown=True):
"""return the user associated to the given session identifier"""
- if self._shutting_down:
+ if checkshuttingdown and self._shutting_down:
raise Exception('Repository is shutting down')
try:
session = self._sessions[sessionid]
@@ -793,10 +794,10 @@
raise UnknownEid(eid)
return extid
- def extid2eid(self, source, lid, etype, session=None, insert=True,
+ def extid2eid(self, source, extid, etype, session=None, insert=True,
recreate=False):
"""get eid from a local id. An eid is attributed if no record is found"""
- cachekey = (str(lid), source.uri)
+ cachekey = (extid, source.uri)
try:
return self._extid_cache[cachekey]
except KeyError:
@@ -805,17 +806,17 @@
if session is None:
session = self.internal_session()
reset_pool = True
- eid = self.system_source.extid2eid(session, source, lid)
+ eid = self.system_source.extid2eid(session, source, extid)
if eid is not None:
self._extid_cache[cachekey] = eid
- self._type_source_cache[eid] = (etype, source.uri, lid)
+ self._type_source_cache[eid] = (etype, source.uri, extid)
if recreate:
- entity = source.before_entity_insertion(session, lid, etype, eid)
+ entity = source.before_entity_insertion(session, extid, etype, eid)
entity._cw_recreating = True
if source.should_call_hooks:
self.hm.call_hooks('before_add_entity', etype, session, entity)
# XXX add fti op ?
- source.after_entity_insertion(session, lid, entity)
+ source.after_entity_insertion(session, extid, entity)
if source.should_call_hooks:
self.hm.call_hooks('after_add_entity', etype, session, entity)
if reset_pool:
@@ -823,7 +824,7 @@
return eid
if not insert:
return
- # no link between lid and eid, create one using an internal session
+ # no link between extid and eid, create one using an internal session
# since the current session user may not have required permissions to
# do necessary stuff and we don't want to commit user session.
#
@@ -835,13 +836,13 @@
try:
eid = self.system_source.create_eid(session)
self._extid_cache[cachekey] = eid
- self._type_source_cache[eid] = (etype, source.uri, lid)
- entity = source.before_entity_insertion(session, lid, etype, eid)
+ self._type_source_cache[eid] = (etype, source.uri, extid)
+ entity = source.before_entity_insertion(session, extid, etype, eid)
if source.should_call_hooks:
self.hm.call_hooks('before_add_entity', etype, session, entity)
# XXX call add_info with complete=False ?
- self.add_info(session, entity, source, lid)
- source.after_entity_insertion(session, lid, entity)
+ self.add_info(session, entity, source, extid)
+ source.after_entity_insertion(session, extid, entity)
if source.should_call_hooks:
self.hm.call_hooks('after_add_entity', etype, session, entity)
else:
--- a/server/schemaserial.py Wed May 27 08:39:16 2009 +0200
+++ b/server/schemaserial.py Wed May 27 13:01:32 2009 +0200
@@ -51,7 +51,7 @@
def _set_sql_prefix(prefix):
"""3.2.0 migration function: allow to unset/reset SQL_PREFIX"""
for module in ('checkintegrity', 'migractions', 'schemahooks',
- 'sources.rql2sql', 'sources.native'):
+ 'sources.rql2sql', 'sources.native', 'sqlutils'):
try:
sys.modules['cubicweb.server.%s' % module].SQL_PREFIX = prefix
print 'changed SQL_PREFIX for %s' % module
--- a/server/sources/ldapuser.py Wed May 27 08:39:16 2009 +0200
+++ b/server/sources/ldapuser.py Wed May 27 13:01:32 2009 +0200
@@ -20,6 +20,8 @@
FOR A PARTICULAR PURPOSE.
"""
+from base64 import b64decode
+
from logilab.common.textutils import get_csv
from rql.nodes import Relation, VariableRef, Constant, Function
@@ -151,7 +153,8 @@
def init(self):
"""method called by the repository once ready to handle request"""
self.repo.looping_task(self._interval, self.synchronize)
- self.repo.looping_task(self._query_cache.ttl.seconds/10, self._query_cache.clear_expired)
+ self.repo.looping_task(self._query_cache.ttl.seconds/10,
+ self._query_cache.clear_expired)
def synchronize(self):
"""synchronize content known by this repository with content in the
@@ -166,7 +169,8 @@
try:
cursor = session.system_sql("SELECT eid, extid FROM entities WHERE "
"source='%s'" % self.uri)
- for eid, extid in cursor.fetchall():
+ for eid, b64extid in cursor.fetchall():
+ extid = b64decode(b64extid)
# if no result found, _search automatically delete entity information
res = self._search(session, extid, BASE)
if res:
--- a/server/sources/native.py Wed May 27 08:39:16 2009 +0200
+++ b/server/sources/native.py Wed May 27 13:01:32 2009 +0200
@@ -1,5 +1,11 @@
"""Adapters for native cubicweb sources.
+Notes:
+* extid (aka external id, the primary key of an entity in the external source
+ from which it comes from) are stored in a varchar column encoded as a base64
+ string. This is because it should actually be Bytes but we want an index on
+ it for fast querying.
+
:organization: Logilab
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
@@ -8,6 +14,7 @@
from threading import Lock
from datetime import datetime
+from base64 import b64decode, b64encode
from logilab.common.cache import Cache
from logilab.common.configuration import REQUIRED
@@ -234,7 +241,7 @@
pwd = rset[0][0]
except IndexError:
raise AuthenticationError('bad login')
- # passwords are stored using the bytea type, so we get a StringIO
+ # passwords are stored using the Bytes type, so we get a StringIO
if pwd is not None:
args['pwd'] = crypt_password(password, pwd.getvalue()[:2])
# get eid from login and (crypted) password
@@ -456,22 +463,25 @@
raise UnknownEid(eid)
if res is None:
raise UnknownEid(eid)
+ if res[-1] is not None:
+ if not isinstance(res, list):
+ res = list(res)
+ res[-1] = b64decode(res[-1])
return res
- def extid2eid(self, session, source, lid):
- """get eid from a local id. An eid is attributed if no record is found"""
+ def extid2eid(self, session, source, extid):
+ """get eid from an external id. Return None if no record found."""
+ assert isinstance(extid, str)
cursor = session.system_sql('SELECT eid FROM entities WHERE '
'extid=%(x)s AND source=%(s)s',
- # str() necessary with pg 8.3
- {'x': str(lid), 's': source.uri})
+ {'x': b64encode(extid), 's': source.uri})
# XXX testing rowcount cause strange bug with sqlite, results are there
# but rowcount is 0
#if cursor.rowcount > 0:
try:
result = cursor.fetchone()
if result:
- eid = result[0]
- return eid
+ return result[0]
except:
pass
return None
@@ -499,8 +509,11 @@
def add_info(self, session, entity, source, extid=None):
"""add type and source info for an eid into the system table"""
# begin by inserting eid/type/source/extid into the entities table
- attrs = {'type': str(entity.e_schema), 'eid': entity.eid,
- 'extid': extid, 'source': source.uri, 'mtime': datetime.now()}
+ if extid is not None:
+ assert isinstance(extid, str)
+ extid = b64encode(extid)
+ attrs = {'type': entity.id, 'eid': entity.eid, 'extid': extid,
+ 'source': source.uri, 'mtime': datetime.now()}
session.system_sql(self.sqlgen.insert('entities', attrs), attrs)
def delete_info(self, session, eid, etype, uri, extid):
@@ -510,6 +523,9 @@
attrs = {'eid': eid}
session.system_sql(self.sqlgen.delete('entities', attrs), attrs)
if self.has_deleted_entitites_table:
+ if extid is not None:
+ assert isinstance(extid, str), type(extid)
+ extid = b64encode(extid)
attrs = {'type': etype, 'eid': eid, 'extid': extid,
'source': uri, 'dtime': datetime.now()}
session.system_sql(self.sqlgen.insert('deleted_entities', attrs), attrs)
--- a/server/sources/pyrorql.py Wed May 27 08:39:16 2009 +0200
+++ b/server/sources/pyrorql.py Wed May 27 13:01:32 2009 +0200
@@ -8,9 +8,9 @@
import threading
from os.path import join
-
from time import mktime
from datetime import datetime
+from base64 import b64decode
from Pyro.errors import PyroError, ConnectionClosedError
@@ -177,7 +177,7 @@
try:
exturi = cnx.describe(extid)[1]
if exturi == 'system' or not exturi in repo.sources_by_uri:
- eid = self.extid2eid(extid, etype, session)
+ eid = self.extid2eid(str(extid), etype, session)
rset = session.eid_rset(eid, etype)
entity = rset.get_entity(0, 0)
entity.complete(entity.e_schema.indexable_attributes())
@@ -188,7 +188,8 @@
continue
for etype, extid in deleted:
try:
- eid = self.extid2eid(extid, etype, session, insert=False)
+ eid = self.extid2eid(str(extid), etype, session,
+ insert=False)
# entity has been deleted from external repository but is not known here
if eid is not None:
repo.delete_info(session, eid)
@@ -307,7 +308,8 @@
etype = descr[rowindex][colindex]
exttype, exturi, extid = cnx.describe(row[colindex])
if exturi == 'system' or not exturi in self.repo.sources_by_uri:
- eid = self.extid2eid(row[colindex], etype, session)
+ eid = self.extid2eid(str(row[colindex]), etype,
+ session)
row[colindex] = eid
else:
# skip this row
@@ -494,7 +496,7 @@
# XXX what about optional relation or outer NOT EXISTS()
raise
except ReplaceByInOperator, ex:
- rhs = 'IN (%s)' % ','.join(str(eid) for eid in ex.eids)
+ rhs = 'IN (%s)' % ','.join(eid for eid in ex.eids)
self.need_translation = False
self.current_operator = None
if node.optional in ('right', 'both'):
@@ -567,17 +569,15 @@
except UnknownEid:
operator = self.current_operator
if operator is not None and operator != '=':
- # deal with query like X eid > 12
+ # deal with query like "X eid > 12"
#
- # The problem is
- # that eid order in the external source may differ from the
- # local source
+ # The problem is that eid order in the external source may
+ # differ from the local source
#
- # So search for all eids from this
- # source matching the condition locally and then to replace the
- # > 12 branch by IN (eids) (XXX we may have to insert a huge
- # number of eids...)
- # planner so that
+ # So search for all eids from this source matching the condition
+ # locally and then to replace the "> 12" branch by "IN (eids)"
+ #
+ # XXX we may have to insert a huge number of eids...)
sql = "SELECT extid FROM entities WHERE source='%s' AND type IN (%s) AND eid%s%s"
etypes = ','.join("'%s'" % etype for etype in self.current_etypes)
cu = self._session.system_sql(sql % (self.source.uri, etypes,
@@ -586,6 +586,6 @@
# results
rows = cu.fetchall()
if rows:
- raise ReplaceByInOperator((r[0] for r in rows))
+ raise ReplaceByInOperator((b64decode(r[0]) for r in rows))
raise
--- a/server/sqlutils.py Wed May 27 08:39:16 2009 +0200
+++ b/server/sqlutils.py Wed May 27 13:01:32 2009 +0200
@@ -111,13 +111,14 @@
w(indexer.sql_drop_fti())
w('')
w(dropschema2sql(schema, prefix=SQL_PREFIX,
- skip_entities=skip_entities, skip_relations=skip_relations))
+ skip_entities=skip_entities,
+ skip_relations=skip_relations))
return '\n'.join(output)
try:
from mx.DateTime import DateTimeType, DateTimeDeltaType
except ImportError:
- DateTimeType, DateTimeDeltaType = None
+ DateTimeType = DateTimeDeltaType = None
class SQLAdapterMixIn(object):
"""Mixin for SQL data sources, getting a connection from a configuration
--- a/server/test/unittest_multisources.py Wed May 27 08:39:16 2009 +0200
+++ b/server/test/unittest_multisources.py Wed May 27 13:01:32 2009 +0200
@@ -77,7 +77,7 @@
'type': u'Card', 'extid': None})
externent = rset.get_entity(3, 0)
metainf = externent.metainformation()
- self.assertEquals(metainf['source'], {'adapter': 'pyrorql', 'base-url': 'http://extern.org', 'uri': 'extern'})
+ self.assertEquals(metainf['source'], {'adapter': 'pyrorql', 'base-url': 'http://extern.org/', 'uri': 'extern'})
self.assertEquals(metainf['type'], 'Card')
self.assert_(metainf['extid'])
etype = self.execute('Any ETN WHERE X is ET, ET name ETN, X eid %(x)s',
@@ -149,7 +149,7 @@
self.execute('Any X ORDERBY DUMB_SORT(RF) WHERE X title RF')
def test_in_eid(self):
- iec1 = self.repo.extid2eid(self.repo.sources_by_uri['extern'], ec1,
+ iec1 = self.repo.extid2eid(self.repo.sources_by_uri['extern'], str(ec1),
'Card', self.session)
rset = self.execute('Any X WHERE X eid IN (%s, %s)' % (iec1, self.ic1))
self.assertEquals(sorted(r[0] for r in rset.rows), sorted([iec1, self.ic1]))
--- a/server/test/unittest_querier.py Wed May 27 08:39:16 2009 +0200
+++ b/server/test/unittest_querier.py Wed May 27 13:01:32 2009 +0200
@@ -6,7 +6,7 @@
from logilab.common.testlib import TestCase, unittest_main
from rql import BadRQLQuery, RQLSyntaxError
-from cubicweb import QueryError, Unauthorized
+from cubicweb import QueryError, Unauthorized, Binary
from cubicweb.server.sqlutils import SQL_PREFIX
from cubicweb.server.utils import crypt_password
from cubicweb.server.sources.native import make_schema
@@ -208,6 +208,13 @@
# should return an empty result set
self.failIf(self.execute('Any X WHERE X eid 99999999'))
+ def test_bytes_storage(self):
+ feid = self.execute('INSERT File X: X name "foo.pdf", X data_format "text/plain", X data %(data)s',
+ {'data': Binary("xxx")})[0][0]
+ fdata = self.execute('Any D WHERE X data D, X eid %(x)s', {'x': feid}, 'x')[0][0]
+ self.assertIsInstance(fdata, Binary)
+ self.assertEquals(fdata.getvalue(), 'xxx')
+
# selection queries tests #################################################
def test_select_1(self):
@@ -470,7 +477,7 @@
self.assertEquals(rset.rows[0][0], self.ueid)
def test_select_complex_sort(self):
- self.skip('retry me once http://www.sqlite.org/cvstrac/tktview?tn=3773 is fixed')
+ """need sqlite including http://www.sqlite.org/cvstrac/tktview?tn=3773 fix"""
rset = self.execute('Any X ORDERBY X,D LIMIT 5 WHERE X creation_date D')
result = rset.rows
result.sort()
@@ -1073,7 +1080,7 @@
cursor = self.pool['system']
cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
% (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
- passwd = cursor.fetchone()[0].getvalue()
+ passwd = str(cursor.fetchone()[0])
self.assertEquals(passwd, crypt_password('toto', passwd[:2]))
rset = self.execute("Any X WHERE X is CWUser, X login 'bob', X upassword '%s'" % passwd)
self.assertEquals(len(rset.rows), 1)
@@ -1087,7 +1094,7 @@
{'pwd': 'tutu'})
cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
% (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
- passwd = cursor.fetchone()[0].getvalue()
+ passwd = str(cursor.fetchone()[0])
self.assertEquals(passwd, crypt_password('tutu', passwd[:2]))
rset = self.execute("Any X WHERE X is CWUser, X login 'bob', X upassword '%s'" % passwd)
self.assertEquals(len(rset.rows), 1)
@@ -1212,8 +1219,9 @@
cause: old variable ref inserted into a fresh rqlst copy
(in RQLSpliter._complex_select_plan)
+
+ need sqlite including http://www.sqlite.org/cvstrac/tktview?tn=3773 fix
"""
- self.skip('retry me once http://www.sqlite.org/cvstrac/tktview?tn=3773 is fixed')
self.execute('Any X ORDERBY D DESC WHERE X creation_date D')
def test_nonregr_extra_joins(self):
--- a/server/test/unittest_repository.py Wed May 27 08:39:16 2009 +0200
+++ b/server/test/unittest_repository.py Wed May 27 13:01:32 2009 +0200
@@ -211,11 +211,8 @@
t = threading.Thread(target=close_in_a_few_moment)
t.start()
try:
- print 'execute'
repo.execute(cnxid, 'DELETE CWUser X WHERE X login "toto"')
- print 'commit'
repo.commit(cnxid)
- print 'commited'
finally:
t.join()
@@ -286,7 +283,8 @@
repo = self.repo
cnxid = repo.connect(*self.default_user_password())
session = repo._get_session(cnxid, setpool=True)
- self.assertEquals(repo.type_and_source_from_eid(1, session), ('CWGroup', 'system', None))
+ self.assertEquals(repo.type_and_source_from_eid(1, session),
+ ('CWGroup', 'system', None))
self.assertEquals(repo.type_from_eid(1, session), 'CWGroup')
self.assertEquals(repo.source_from_eid(1, session).uri, 'system')
self.assertEquals(repo.eid2extid(repo.system_source, 1, session), None)
--- a/skeleton/__pkginfo__.py.tmpl Wed May 27 08:39:16 2009 +0200
+++ b/skeleton/__pkginfo__.py.tmpl Wed May 27 13:01:32 2009 +0200
@@ -32,7 +32,7 @@
return [join(dirpath, fname) for fname in _listdir(dirpath)
if fname[0] != '.' and not fname.endswith('.pyc')
and not fname.endswith('~')
- and not isdir(join(dirpath, fname))]¶
+ and not isdir(join(dirpath, fname))]
from glob import glob
try:
--- a/vregistry.py Wed May 27 08:39:16 2009 +0200
+++ b/vregistry.py Wed May 27 13:01:32 2009 +0200
@@ -237,7 +237,7 @@
# XXX automatic reloading management
try:
registry[obj.id].remove(registered)
- except ValueError:
+ except KeyError:
self.warning('can\'t remove %s, no id %s in the %s registry',
removed_id, obj.id, registryname)
except ValueError:
@@ -254,11 +254,14 @@
replaced = replaced.classid()
registryname = registryname or obj.__registry__
registry = self.registry(registryname)
- registered_objs = registry[obj.id]
+ registered_objs = registry.get(obj.id, ())
for index, registered in enumerate(registered_objs):
if registered.classid() == replaced:
del registry[obj.id][index]
break
+ else:
+ self.warning('trying to replace an unregistered view %s by %s',
+ replaced, obj)
self.register(obj, registryname=registryname)
# dynamic selection methods ###############################################
Binary file web/data/favicon.ico has changed
--- a/web/form.py Wed May 27 08:39:16 2009 +0200
+++ b/web/form.py Wed May 27 13:01:32 2009 +0200
@@ -270,13 +270,21 @@
@iclassmethod
def field_by_name(cls_or_self, name, role='subject'):
- """return field with the given name and role"""
+ """return field with the given name and role.
+ Raise FieldNotFound if the field can't be found.
+ """
for field in cls_or_self._fieldsattr():
if field.name == name and field.role == role:
return field
raise FieldNotFound(name)
@iclassmethod
+ def fields_by_name(cls_or_self, name, role='subject'):
+ """return a list of fields with the given name and role"""
+ return [field for field in cls_or_self._fieldsattr()
+ if field.name == name and field.role == role]
+
+ @iclassmethod
def remove_field(cls_or_self, field):
"""remove a field from form class or instance"""
cls_or_self._fieldsattr().remove(field)
@@ -341,7 +349,9 @@
# ensure rendervalues is a dict
if rendervalues is None:
rendervalues = {}
- for field in self.fields:
+ # use a copy in case fields are modified while context is build (eg
+ # __linkto handling for instance)
+ for field in self.fields[:]:
for field in field.actual_fields(self):
field.form_init(self)
value = self.form_field_display_value(field, rendervalues)
@@ -443,12 +453,13 @@
self.edited_entity = self.complete_entity(self.row or 0, self.col or 0)
self.form_add_hidden('__type', eidparam=True)
self.form_add_hidden('eid')
- if msg is not None:
+ if msg:
# If we need to directly attach the new object to another one
+ self.form_add_hidden('__message', msg)
+ if not self.is_subform:
for linkto in self.req.list_form_param('__linkto'):
self.form_add_hidden('__linkto', linkto)
msg = '%s %s' % (msg, self.req._('and linked'))
- self.form_add_hidden('__message', msg)
# in case of direct instanciation
self.schema = self.edited_entity.schema
self.vreg = self.edited_entity.vreg
@@ -478,7 +489,16 @@
def _req_display_value(self, field):
value = super(EntityFieldsForm, self)._req_display_value(field)
if value is None:
- value = self.edited_entity.linked_to(field.name, field.role) or None
+ value = self.edited_entity.linked_to(field.name, field.role)
+ if value:
+ searchedvalues = ['%s:%s:%s' % (field.name, eid, field.role)
+ for eid in value]
+ # remove associated __linkto hidden fields
+ for field in self.fields_by_name('__linkto'):
+ if field.initial in searchedvalues:
+ self.remove_field(field)
+ else:
+ value = None
return value
def _form_field_default_value(self, field, load_bytes):
--- a/web/formrenderers.py Wed May 27 08:39:16 2009 +0200
+++ b/web/formrenderers.py Wed May 27 13:01:32 2009 +0200
@@ -83,7 +83,7 @@
return tags.label(label, **attrs)
def render_help(self, form, field):
- help = []
+ help = []
descr = field.help
if descr:
help.append('<div class="helper">%s</div>' % form.req._(descr))
--- a/web/test/unittest_form.py Wed May 27 08:39:16 2009 +0200
+++ b/web/test/unittest_form.py Wed May 27 13:01:32 2009 +0200
@@ -73,6 +73,21 @@
form.form_build_context({})
self.assertEquals(form.form_field_display_value(field, {}), 'toto')
+
+ def test_linkto_field_duplication(self):
+ e = self.etype_instance('CWUser')
+ e.eid = 'A'
+ e.req = self.req
+ geid = self.execute('CWGroup X WHERE X name "users"')[0][0]
+ self.req.form['__linkto'] = 'in_group:%s:subject' % geid
+ form = self.vreg.select_object('forms', 'edition', self.req, None, entity=e)
+ form.content_type = 'text/html'
+ pageinfo = self._check_html(form.form_render(), form, template=None)
+ inputs = pageinfo.find_tag('select', False)
+ self.failUnless(any(attrs for t, attrs in inputs if attrs.get('name') == 'in_group:A'))
+ inputs = pageinfo.find_tag('input', False)
+ self.failIf(any(attrs for t, attrs in inputs if attrs.get('name') == '__linkto'))
+
# form view tests #########################################################
def test_massmailing_formview(self):
--- a/web/test/unittest_viewselector.py Wed May 27 08:39:16 2009 +0200
+++ b/web/test/unittest_viewselector.py Wed May 27 13:01:32 2009 +0200
@@ -1,8 +1,6 @@
# -*- coding: iso-8859-1 -*-
"""XXX rename, split, reorganize this
"""
-from __future__ import with_statement
-
from logilab.common.testlib import unittest_main
from cubicweb.devtools.apptest import EnvBasedTC
--- a/web/uicfg.py Wed May 27 08:39:16 2009 +0200
+++ b/web/uicfg.py Wed May 27 13:01:32 2009 +0200
@@ -66,13 +66,10 @@
"""
__docformat__ = "restructuredtext en"
+from cubicweb import neg_role
from cubicweb.rtags import RelationTags, RelationTagsBool, RelationTagsSet
from cubicweb.web import formwidgets
-# primary view configuration ##################################################
-
-def dual_role(role):
- return role == 'subject' and 'object' or 'subject'
def card_from_role(card, role):
if role == 'subject':
@@ -80,10 +77,12 @@
assert role in ('object', 'sobject'), repr(role)
return card[1]
+# primary view configuration ##################################################
+
def init_primaryview_section(rtag, sschema, rschema, oschema, role):
if rtag.get(sschema, rschema, oschema, role) is None:
card = card_from_role(rschema.rproperty(sschema, oschema, 'cardinality'), role)
- composed = rschema.rproperty(sschema, oschema, 'composite') == dual_role(role)
+ composed = rschema.rproperty(sschema, oschema, 'composite') == neg_role(role)
if rschema.is_final():
if rschema.meta or oschema.type in ('Password', 'Bytes'):
section = 'hidden'
--- a/web/views/basecomponents.py Wed May 27 08:39:16 2009 +0200
+++ b/web/views/basecomponents.py Wed May 27 13:01:32 2009 +0200
@@ -8,6 +8,7 @@
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
__docformat__ = "restructuredtext en"
+_ = unicode
from rql import parse
@@ -18,8 +19,6 @@
from cubicweb.web.htmlwidgets import (MenuWidget, PopupBoxMenu, BoxSeparator,
BoxLink)
-_ = unicode
-
VISIBLE_PROP_DEF = {
_('visible'): dict(type='Boolean', default=False,
help=_('display the component or not')),
--- a/web/views/editcontroller.py Wed May 27 08:39:16 2009 +0200
+++ b/web/views/editcontroller.py Wed May 27 13:01:32 2009 +0200
@@ -5,6 +5,7 @@
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
__docformat__ = "restructuredtext en"
+
from decimal import Decimal
from rql.utils import rqlvar_maker
@@ -228,6 +229,9 @@
formparams['name'] = value[0]
self.relations.append('X name %(name)s')
value = val
+ else:
+ # no specified value, skip
+ return
elif value is not None:
if attrtype in ('Date', 'Datetime', 'Time'):
try:
--- a/web/views/management.py Wed May 27 08:39:16 2009 +0200
+++ b/web/views/management.py Wed May 27 13:01:32 2009 +0200
@@ -23,6 +23,7 @@
class SecurityViewMixIn(object):
"""display security information for a given schema """
+
def schema_definition(self, eschema, link=True, access_types=None):
w = self.w
_ = self.req._
@@ -40,7 +41,7 @@
for trad, group in sorted(groups):
if link:
l.append(u'<a href="%s" class="%s">%s</a><br/>' % (
- self.build_url('egroup/%s' % group), group, trad))
+ self.build_url('cwgroup/%s' % group), group, trad))
else:
l.append(u'<div class="%s">%s</div>' % (group, trad))
w(u'<td>%s</td>' % u''.join(l))
@@ -68,6 +69,7 @@
__select__ = EntityView.__select__ & authenticated_user()
title = _('security')
+
def call(self):
self.w(u'<div id="progress">%s</div>' % self.req._('validating...'))
super(SecurityManagementView, self).call()