--- a/common/__init__.py Mon Jun 29 14:12:18 2009 +0200
+++ b/common/__init__.py Thu Jul 02 10:35:03 2009 +0200
@@ -18,9 +18,9 @@
rtype = 'String'
@classmethod
- def st_description(cls, funcnode):
- return ', '.join(term.get_description()
- for term in iter_funcnode_variables(funcnode))
+ def st_description(cls, funcnode, mainindex, tr):
+ return ', '.join(sorted(term.get_description(mainindex, tr)
+ for term in iter_funcnode_variables(funcnode)))
register_function(COMMA_JOIN) # XXX do not expose?
@@ -41,8 +41,8 @@
rtype = 'String'
@classmethod
- def st_description(cls, funcnode):
- return funcnode.children[0].get_description()
+ def st_description(cls, funcnode, mainindex, tr):
+ return funcnode.children[0].get_description(mainindex, tr)
register_function(LIMIT_SIZE)
--- a/common/uilib.py Mon Jun 29 14:12:18 2009 +0200
+++ b/common/uilib.py Thu Jul 02 10:35:03 2009 +0200
@@ -92,7 +92,9 @@
# fallback implementation, nicer one defined below if lxml is available
def soup2xhtml(data, encoding):
- return data
+ # normalize line break
+ # see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1
+ return u'\n'.join(data.splitlines())
# fallback implementation, nicer one defined below if lxml> 2.0 is available
def safe_cut(text, length):
@@ -123,6 +125,10 @@
Note: the function considers a string with no surrounding tag as valid
if <div>`data`</div> can be parsed by an XML parser
"""
+ # normalize line break
+ # see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1
+ data = u'\n'.join(data.splitlines())
+ # XXX lxml 1.1 support still needed ?
xmltree = etree.HTML('<div>%s</div>' % data)
# NOTE: lxml 1.1 (etch platforms) doesn't recognize
# the encoding=unicode parameter (lxml 2.0 does), this is
--- a/cwconfig.py Mon Jun 29 14:12:18 2009 +0200
+++ b/cwconfig.py Thu Jul 02 10:35:03 2009 +0200
@@ -17,6 +17,8 @@
import sys
import os
import logging
+from smtplib import SMTP
+from threading import Lock
from os.path import exists, join, expanduser, abspath, normpath, basename, isdir
from logilab.common.decorators import cached
@@ -29,6 +31,8 @@
CONFIGURATIONS = []
+SMTP_LOCK = Lock()
+
class metaconfiguration(type):
"""metaclass to automaticaly register configuration"""
@@ -743,7 +747,7 @@
self.warning('site_erudi.py is deprecated, should be renamed to site_cubicweb.py')
def _load_site_cubicweb(self, sitefile):
- context = {}
+ context = {'__file__': sitefile}
execfile(sitefile, context, context)
self.info('%s loaded', sitefile)
# cube specific options
@@ -827,6 +831,28 @@
sourcedirs.append(self.i18n_lib_dir())
return i18n.compile_i18n_catalogs(sourcedirs, i18ndir, langs)
+ def sendmails(self, msgs):
+ """msgs: list of 2-uple (message object, recipients)"""
+ server, port = self['smtp-host'], self['smtp-port']
+ SMTP_LOCK.acquire()
+ try:
+ try:
+ smtp = SMTP(server, port)
+ except Exception, ex:
+ self.exception("can't connect to smtp server %s:%s (%s)",
+ server, port, ex)
+ return
+ heloaddr = '%s <%s>' % (self['sender-name'], self['sender-addr'])
+ for msg, recipients in msgs:
+ try:
+ smtp.sendmail(heloaddr, recipients, msg.as_string())
+ except Exception, ex:
+ self.exception("error sending mail to %s (%s)",
+ recipients, ex)
+ smtp.close()
+ finally:
+ SMTP_LOCK.release()
+
set_log_methods(CubicWebConfiguration, logging.getLogger('cubicweb.configuration'))
# alias to get a configuration instance from an application id
--- a/cwvreg.py Mon Jun 29 14:12:18 2009 +0200
+++ b/cwvreg.py Thu Jul 02 10:35:03 2009 +0200
@@ -303,9 +303,10 @@
def user_property_keys(self, withsitewide=False):
if withsitewide:
- return sorted(self['propertydefs'])
+ return sorted(k for k in self['propertydefs']
+ if not k.startswith('sources.'))
return sorted(k for k, kd in self['propertydefs'].iteritems()
- if not kd['sitewide'])
+ if not kd['sitewide'] and not k.startswith('sources.'))
def register_property(self, key, type, help, default=None, vocabulary=None,
sitewide=False):
--- a/debian/control Mon Jun 29 14:12:18 2009 +0200
+++ b/debian/control Thu Jul 02 10:35:03 2009 +0200
@@ -76,7 +76,7 @@
Package: cubicweb-common
Architecture: all
XB-Python-Version: ${python:Versions}
-Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.41.0), python-yams (>= 0.23.0), python-rql (>= 0.22.0)
+Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.41.0), python-yams (>= 0.23.0), python-rql (>= 0.22.1)
Recommends: python-simpletal (>= 4.0), python-lxml
Conflicts: cubicweb-core
Replaces: cubicweb-core
--- a/devtools/apptest.py Mon Jun 29 14:12:18 2009 +0200
+++ b/devtools/apptest.py Thu Jul 02 10:35:03 2009 +0200
@@ -46,8 +46,8 @@
def sendmail(self, helo_addr, recipients, msg):
MAILBOX.append(Email(recipients, msg))
-from cubicweb.server import hookhelper
-hookhelper.SMTP = MockSMTP
+from cubicweb import cwconfig
+cwconfig.SMTP = MockSMTP
def get_versions(self, checkversions=False):
--- a/devtools/repotest.py Mon Jun 29 14:12:18 2009 +0200
+++ b/devtools/repotest.py Thu Jul 02 10:35:03 2009 +0200
@@ -208,6 +208,7 @@
class BasePlannerTC(BaseQuerierTC):
+ newsources = 0
def setup(self):
clear_cache(self.repo, 'rel_type_sources')
clear_cache(self.repo, 'rel_type_sources')
@@ -220,7 +221,6 @@
self.schema = self.o.schema
self.sources = self.o._repo.sources
self.system = self.sources[-1]
- self.newsources = 0
do_monkey_patch()
def add_source(self, sourcecls, uri):
--- a/devtools/testlib.py Mon Jun 29 14:12:18 2009 +0200
+++ b/devtools/testlib.py Thu Jul 02 10:35:03 2009 +0200
@@ -249,6 +249,8 @@
def iter_automatic_rsets(self, limit=10):
"""generates basic resultsets for each entity type"""
etypes = self.to_test_etypes()
+ if not etypes:
+ return
for etype in etypes:
yield self.execute('Any X LIMIT %s WHERE X is %s' % (limit, etype))
etype1 = etypes.pop()
--- a/schema.py Mon Jun 29 14:12:18 2009 +0200
+++ b/schema.py Thu Jul 02 10:35:03 2009 +0200
@@ -14,6 +14,7 @@
from warnings import warn
from logilab.common.decorators import cached, clear_cache, monkeypatch
+from logilab.common.deprecation import obsolete
from logilab.common.compat import any
from yams import BadSchemaDefinition, buildobjs as ybo
@@ -97,6 +98,16 @@
yams_add_relation(relations, format_attrdef, name+'_format', insertidx)
yams_add_relation(relations, rdef, name, insertidx)
+
+yams_EntityType_add_relation = ybo.EntityType.add_relation
+@monkeypatch(ybo.EntityType)
+def add_relation(self, rdef, name=None):
+ yams_EntityType_add_relation(self, rdef, name)
+ if isinstance(rdef, RichString) and not rdef in self._defined:
+ format_attr_name = (name or rdef.name) + '_format'
+ rdef = self.get_relations(format_attr_name).next()
+ self._ensure_relation_type(rdef)
+
def display_name(req, key, form=''):
"""return a internationalized string for the key (schema entity or relation
name) in a given form
@@ -109,7 +120,7 @@
# ensure unicode
# added .lower() in case no translation are available
return unicode(req._(key)).lower()
-__builtins__['display_name'] = display_name
+__builtins__['display_name'] = obsolete('display_name should be imported from cubicweb.schema')(display_name)
def ERSchema_display_name(self, req, form=''):
"""return a internationalized string for the entity/relation type name in
--- a/schemas/workflow.py Mon Jun 29 14:12:18 2009 +0200
+++ b/schemas/workflow.py Thu Jul 02 10:35:03 2009 +0200
@@ -60,7 +60,7 @@
transition_of = SubjectRelation('CWEType', cardinality='+*',
description=_('entity types which may use this transition'),
constraints=[RQLConstraint('O final FALSE')])
- destination_state = SubjectRelation('State', cardinality='?*',
+ destination_state = SubjectRelation('State', cardinality='1*',
constraints=[RQLConstraint('S transition_of ET, O state_of ET')],
description=_('destination state for this transition'))
--- a/server/hookhelper.py Mon Jun 29 14:12:18 2009 +0200
+++ b/server/hookhelper.py Thu Jul 02 10:35:03 2009 +0200
@@ -7,9 +7,6 @@
"""
__docformat__ = "restructuredtext en"
-from smtplib import SMTP
-from threading import Lock
-
from cubicweb import RepositoryError
from cubicweb.server.pool import SingleLastOperation
@@ -47,8 +44,6 @@
# mail related ################################################################
-SMTP_LOCK = Lock()
-
class SendMailOp(SingleLastOperation):
def __init__(self, session, msg=None, recipients=None, **kwargs):
# may not specify msg yet, as
@@ -70,26 +65,7 @@
self.repo.threaded_task(self.sendmails)
def sendmails(self):
- server, port = self.config['smtp-host'], self.config['smtp-port']
- SMTP_LOCK.acquire()
- try:
- try:
- smtp = SMTP(server, port)
- except Exception, ex:
- self.exception("can't connect to smtp server %s:%s (%s)",
- server, port, ex)
- return
- heloaddr = '%s <%s>' % (self.config['sender-name'],
- self.config['sender-addr'])
- for msg, recipients in self.to_send:
- try:
- smtp.sendmail(heloaddr, recipients, msg.as_string())
- except Exception, ex:
- self.exception("error sending mail to %s (%s)",
- recipients, ex)
- smtp.close()
- finally:
- SMTP_LOCK.release()
+ self.config.sendmails(self.to_send)
# state related ###############################################################
--- a/server/hooks.py Mon Jun 29 14:12:18 2009 +0200
+++ b/server/hooks.py Thu Jul 02 10:35:03 2009 +0200
@@ -85,16 +85,16 @@
"""
ftcontainer = session.repo.schema.rschema(rtype).fulltext_container
if ftcontainer == 'subject':
- FTIndexEntityOp(session, entity=session.entity(eidto))
+ FTIndexEntityOp(session, entity=session.entity_from_eid(eidto))
elif ftcontainer == 'object':
- FTIndexEntityOp(session, entity=session.entity(eidfrom))
+ FTIndexEntityOp(session, entity=session.entity_from_eid(eidfrom))
def fti_update_after_delete_relation(session, eidfrom, rtype, eidto):
"""sync fulltext index when relevant relation is deleted. Reindexing both
entities is necessary.
"""
if session.repo.schema.rschema(rtype).fulltext_container:
- FTIndexEntityOp(session, entity=session.entity(eidto))
- FTIndexEntityOp(session, entity=session.entity(eidfrom))
+ FTIndexEntityOp(session, entity=session.entity_from_eid(eidto))
+ FTIndexEntityOp(session, entity=session.entity_from_eid(eidfrom))
class SyncOwnersOp(PreCommitOperation):
@@ -379,7 +379,7 @@
etype = session.describe(fromeid)[0]
if not (session.is_super_session or 'managers' in session.user.groups):
if not state is None:
- entity = session.entity(fromeid)
+ entity = session.entity_from_eid(fromeid)
# we should find at least one transition going to this state
try:
iter(state.transitions(entity, toeid)).next()
--- a/server/repository.py Mon Jun 29 14:12:18 2009 +0200
+++ b/server/repository.py Thu Jul 02 10:35:03 2009 +0200
@@ -343,7 +343,6 @@
'connections pools size)')
def _free_pool(self, pool):
- pool.rollback()
self._available_pools.put_nowait(pool)
def pinfo(self):
--- a/server/rqlannotation.py Mon Jun 29 14:12:18 2009 +0200
+++ b/server/rqlannotation.py Thu Jul 02 10:35:03 2009 +0200
@@ -22,21 +22,7 @@
has_text_query = False
need_distinct = rqlst.distinct
for rel in rqlst.iget_nodes(Relation):
- if rel.neged(strict=True):
- if rel.is_types_restriction():
- need_distinct = True
- else:
- rschema = getrschema(rel.r_type)
- if not rschema.is_final():
- if rschema.inlined:
- try:
- var = rel.children[1].children[0].variable
- except AttributeError:
- pass # rewritten variable
- else:
- if not var.stinfo['constnode']:
- need_distinct = True
- elif getrschema(rel.r_type).symetric:
+ if getrschema(rel.r_type).symetric:
for vref in rel.iget_nodes(VariableRef):
stinfo = vref.variable.stinfo
if not stinfo['constnode'] and stinfo['selected']:
--- a/server/schemahooks.py Mon Jun 29 14:12:18 2009 +0200
+++ b/server/schemahooks.py Thu Jul 02 10:35:03 2009 +0200
@@ -38,7 +38,7 @@
def get_constraints(session, entity):
constraints = []
for cstreid in session.transaction_data.get(entity.eid, ()):
- cstrent = session.entity(cstreid)
+ cstrent = session.entity_from_eid(cstreid)
cstr = CONSTRAINTS[cstrent.type].deserialize(cstrent.value)
cstr.eid = cstreid
constraints.append(cstr)
--- a/server/session.py Mon Jun 29 14:12:18 2009 +0200
+++ b/server/session.py Thu Jul 02 10:35:03 2009 +0200
@@ -84,10 +84,6 @@
"""return an entity class for the given entity type"""
return self.vreg.etype_class(etype)
- def entity(self, eid):
- """return a result set for the given eid"""
- return self.eid_rset(eid).get_entity(0, 0)
-
def system_sql(self, sql, args=None):
"""return a sql cursor on the system database"""
if not sql.split(None, 1)[0].upper() == 'SELECT':
@@ -270,6 +266,7 @@
assert not self.pending_operations
self.transaction_data.clear()
self._touch()
+ self.debug('commit session %s done (no db activity)', self.id)
return
if self.commit_state:
return
@@ -311,6 +308,7 @@
assert not self.pending_operations
self.transaction_data.clear()
self._touch()
+ self.debug('rollback session %s done (no db activity)', self.id)
return
try:
while self.pending_operations:
@@ -321,6 +319,7 @@
self.critical('rollback error', exc_info=sys.exc_info())
continue
self.pool.rollback()
+ self.debug('rollback for session %s done', self.id)
finally:
self._touch()
self.pending_operations[:] = []
@@ -359,16 +358,6 @@
self._threaddata.transaction_data = {}
return self._threaddata.transaction_data
- @obsolete('use direct access to session.transaction_data')
- def query_data(self, key, default=None, setdefault=False, pop=False):
- if setdefault:
- assert not pop
- return self.transaction_data.setdefault(key, default)
- if pop:
- return self.transaction_data.pop(key, default)
- else:
- return self.transaction_data.get(key, default)
-
@property
def pending_operations(self):
try:
@@ -457,6 +446,21 @@
description.append(tuple(row_descr))
return description
+ @obsolete('use direct access to session.transaction_data')
+ def query_data(self, key, default=None, setdefault=False, pop=False):
+ if setdefault:
+ assert not pop
+ return self.transaction_data.setdefault(key, default)
+ if pop:
+ return self.transaction_data.pop(key, default)
+ else:
+ return self.transaction_data.get(key, default)
+
+ @obsolete('use entity_from_eid(eid, etype=None)')
+ def entity(self, eid):
+ """return a result set for the given eid"""
+ return self.eid_rset(eid).get_entity(0, 0)
+
class ChildSession(Session):
"""child (or internal) session are used to hijack the security system
--- a/server/sources/rql2sql.py Mon Jun 29 14:12:18 2009 +0200
+++ b/server/sources/rql2sql.py Thu Jul 02 10:35:03 2009 +0200
@@ -490,10 +490,10 @@
sql.insert(1, 'FROM (SELECT 1) AS _T')
sqls.append('\n'.join(sql))
if select.need_intersect:
- if distinct or not self.dbms_helper.intersect_all_support:
- return '\nINTERSECT\n'.join(sqls)
- else:
- return '\nINTERSECT ALL\n'.join(sqls)
+ #if distinct or not self.dbms_helper.intersect_all_support:
+ return '\nINTERSECT\n'.join(sqls)
+ #else:
+ # return '\nINTERSECT ALL\n'.join(sqls)
elif distinct:
return '\nUNION\n'.join(sqls)
else:
@@ -661,13 +661,27 @@
lhsvar, _, rhsvar, rhsconst = relation_info(relation)
# we are sure here to have a lhsvar
assert lhsvar is not None
- lhssql = self._inlined_var_sql(lhsvar, relation.r_type)
if isinstance(relation.parent, Not):
self._state.done.add(relation.parent)
- sql = "%s IS NULL" % lhssql
if rhsvar is not None and not rhsvar._q_invariant:
- sql = '(%s OR %s!=%s)' % (sql, lhssql, rhsvar.accept(self))
+ # if the lhs variable is only linked to this relation, this mean we
+ # only want the relation to NOT exists
+ self._state.push_scope()
+ lhssql = self._inlined_var_sql(lhsvar, relation.r_type)
+ rhssql = rhsvar.accept(self)
+ restrictions, tables = self._state.pop_scope()
+ restrictions.append('%s=%s' % (lhssql, rhssql))
+ if not tables:
+ sql = 'NOT EXISTS(SELECT 1 WHERE %s)' % (
+ ' AND '.join(restrictions))
+ else:
+ sql = 'NOT EXISTS(SELECT 1 FROM %s WHERE %s)' % (
+ ', '.join(tables), ' AND '.join(restrictions))
+ else:
+ lhssql = self._inlined_var_sql(lhsvar, relation.r_type)
+ sql = '%s IS NULL' % self._inlined_var_sql(lhsvar, relation.r_type)
return sql
+ lhssql = self._inlined_var_sql(lhsvar, relation.r_type)
if rhsconst is not None:
return '%s=%s' % (lhssql, rhsconst.accept(self))
if isinstance(rhsvar, Variable) and not rhsvar.name in self._varmap:
--- a/server/test/unittest_extlite.py Mon Jun 29 14:12:18 2009 +0200
+++ b/server/test/unittest_extlite.py Thu Jul 02 10:35:03 2009 +0200
@@ -7,37 +7,32 @@
sqlite_file = '_extlite_test.sqlite'
def setUp(self):
cnx1 = get_connection('sqlite', database=self.sqlite_file)
- print 'SET IP'
cu = cnx1.cursor()
cu.execute('CREATE TABLE toto(name integer);')
cnx1.commit()
cnx1.close()
-
+
def tearDown(self):
try:
os.remove(self.sqlite_file)
except:
pass
+
def test(self):
lock = threading.Lock()
-
+
def run_thread():
- print 'run_thread'
cnx2 = get_connection('sqlite', database=self.sqlite_file)
lock.acquire()
- print 't2 sel1'
cu = cnx2.cursor()
cu.execute('SELECT name FROM toto')
self.failIf(cu.fetchall())
cnx2.commit()
- print 'done'
lock.release()
time.sleep(0.1)
lock.acquire()
- print 't2 sel2'
cu.execute('SELECT name FROM toto')
self.failUnless(cu.fetchall())
- print 'done'
lock.release()
cnx1 = get_connection('sqlite', database=self.sqlite_file)
@@ -45,17 +40,13 @@
thread = threading.Thread(target=run_thread)
thread.start()
cu = cnx1.cursor()
- print 't1 sel'
cu.execute('SELECT name FROM toto')
- print 'done'
lock.release()
time.sleep(0.1)
cnx1.commit()
lock.acquire()
- print 't1 insert'
cu.execute("INSERT INTO toto(name) VALUES ('toto')")
cnx1.commit()
- print 'done'
lock.release()
if __name__ == '__main__':
--- a/server/test/unittest_repository.py Mon Jun 29 14:12:18 2009 +0200
+++ b/server/test/unittest_repository.py Thu Jul 02 10:35:03 2009 +0200
@@ -56,13 +56,12 @@
namecol = SQL_PREFIX + 'name'
finalcol = SQL_PREFIX + 'final'
try:
- sqlcursor = pool['system']
- sqlcursor.execute('SELECT %s FROM %s WHERE %s is NULL' % (
+ cu = self.session.system_sql('SELECT %s FROM %s WHERE %s is NULL' % (
namecol, table, finalcol))
- self.assertEquals(sqlcursor.fetchall(), [])
- sqlcursor.execute('SELECT %s FROM %s WHERE %s=%%(final)s ORDER BY %s'
+ self.assertEquals(cu.fetchall(), [])
+ cu = self.session.system_sql('SELECT %s FROM %s WHERE %s=%%(final)s ORDER BY %s'
% (namecol, table, finalcol, namecol), {'final': 'TRUE'})
- self.assertEquals(sqlcursor.fetchall(), [(u'Boolean',), (u'Bytes',),
+ self.assertEquals(cu.fetchall(), [(u'Boolean',), (u'Bytes',),
(u'Date',), (u'Datetime',),
(u'Decimal',),(u'Float',),
(u'Int',),
@@ -358,38 +357,36 @@
entity.eid = -1
entity.complete = lambda x: None
self.repo.add_info(self.session, entity, self.repo.sources_by_uri['system'])
- cursor = self.session.pool['system']
- cursor.execute('SELECT * FROM entities WHERE eid = -1')
- data = cursor.fetchall()
+ cu = self.session.system_sql('SELECT * FROM entities WHERE eid = -1')
+ data = cu.fetchall()
self.assertIsInstance(data[0][3], datetime)
data[0] = list(data[0])
data[0][3] = None
self.assertEquals(tuplify(data), [(-1, 'Personne', 'system', None, None)])
self.repo.delete_info(self.session, -1)
#self.repo.commit()
- cursor.execute('SELECT * FROM entities WHERE eid = -1')
- data = cursor.fetchall()
+ cu = self.session.system_sql('SELECT * FROM entities WHERE eid = -1')
+ data = cu.fetchall()
self.assertEquals(data, [])
class FTITC(RepositoryBasedTC):
def test_reindex_and_modified_since(self):
- cursor = self.session.pool['system']
eidp = self.execute('INSERT Personne X: X nom "toto", X prenom "tutu"')[0][0]
self.commit()
ts = datetime.now()
self.assertEquals(len(self.execute('Personne X WHERE X has_text "tutu"')), 1)
- cursor.execute('SELECT mtime, eid FROM entities WHERE eid = %s' % eidp)
- omtime = cursor.fetchone()[0]
+ cu = self.session.system_sql('SELECT mtime, eid FROM entities WHERE eid = %s' % eidp)
+ omtime = cu.fetchone()[0]
# our sqlite datetime adapter is ignore seconds fraction, so we have to
# ensure update is done the next seconds
time.sleep(1 - (ts.second - int(ts.second)))
self.execute('SET X nom "tata" WHERE X eid %(x)s', {'x': eidp}, 'x')
self.commit()
self.assertEquals(len(self.execute('Personne X WHERE X has_text "tutu"')), 1)
- cursor.execute('SELECT mtime FROM entities WHERE eid = %s' % eidp)
- mtime = cursor.fetchone()[0]
+ cu = self.session.system_sql('SELECT mtime FROM entities WHERE eid = %s' % eidp)
+ mtime = cu.fetchone()[0]
self.failUnless(omtime < mtime)
self.commit()
date, modified, deleted = self.repo.entities_modified_since(('Personne',), omtime)
--- a/server/test/unittest_rql2sql.py Mon Jun 29 14:12:18 2009 +0200
+++ b/server/test/unittest_rql2sql.py Thu Jul 02 10:35:03 2009 +0200
@@ -251,9 +251,9 @@
# Any O WHERE NOT S corrected_in O, S eid %(x)s, S concerns P, O version_of P, O in_state ST, NOT ST name "published", O modification_date MTIME ORDERBY MTIME DESC LIMIT 9
('Any O WHERE NOT S ecrit_par O, S eid 1, S inline1 P, O inline2 P',
- '''SELECT DISTINCT O.cw_eid
+ '''SELECT O.cw_eid
FROM cw_Note AS S, cw_Personne AS O
-WHERE (S.cw_ecrit_par IS NULL OR S.cw_ecrit_par!=O.cw_eid) AND S.cw_eid=1 AND O.cw_inline2=S.cw_inline1'''),
+WHERE NOT EXISTS(SELECT 1 WHERE S.cw_ecrit_par=O.cw_eid) AND S.cw_eid=1 AND O.cw_inline2=S.cw_inline1'''),
('DISTINCT Any S ORDERBY stockproc(SI) WHERE NOT S ecrit_par O, S para SI',
'''SELECT T1.C0 FROM (SELECT DISTINCT S.cw_eid AS C0, STOCKPROC(S.cw_para) AS C1
@@ -698,7 +698,6 @@
FROM cw_Tag AS S, cw_Tag AS T, tags_relation AS rel_tags0
WHERE NOT (T.cw_eid=28258) AND rel_tags0.eid_from=T.cw_eid AND rel_tags0.eid_to=S.cw_eid'''),
-
('Any X,Y WHERE X created_by Y, X eid 5, NOT Y eid 6',
'''SELECT 5, rel_created_by0.eid_to
FROM created_by_relation AS rel_created_by0
@@ -736,25 +735,25 @@
WHERE NOT EXISTS(SELECT 1 FROM evaluee_relation AS rel_evaluee0,cw_CWUser AS Y WHERE rel_evaluee0.eid_from=Y.cw_eid AND rel_evaluee0.eid_to=X.cw_eid)'''),
('Any X,T WHERE X title T, NOT X is Bookmark',
- '''SELECT DISTINCT X.cw_eid, X.cw_title
+ '''SELECT X.cw_eid, X.cw_title
FROM cw_Card AS X
-UNION
-SELECT DISTINCT X.cw_eid, X.cw_title
+UNION ALL
+SELECT X.cw_eid, X.cw_title
FROM cw_EmailThread AS X'''),
('Any K,V WHERE P is CWProperty, P pkey K, P value V, NOT P for_user U',
- '''SELECT DISTINCT P.cw_pkey, P.cw_value
+ '''SELECT P.cw_pkey, P.cw_value
FROM cw_CWProperty AS P
WHERE P.cw_for_user IS NULL'''),
('Any S WHERE NOT X in_state S, X is IN(Affaire, CWUser)',
- '''SELECT DISTINCT S.cw_eid
-FROM cw_Affaire AS X, cw_State AS S
-WHERE (X.cw_in_state IS NULL OR X.cw_in_state!=S.cw_eid)
+ '''SELECT S.cw_eid
+FROM cw_State AS S
+WHERE NOT EXISTS(SELECT 1 FROM cw_Affaire AS X WHERE X.cw_in_state=S.cw_eid)
INTERSECT
-SELECT DISTINCT S.cw_eid
-FROM cw_CWUser AS X, cw_State AS S
-WHERE (X.cw_in_state IS NULL OR X.cw_in_state!=S.cw_eid)'''),
+SELECT S.cw_eid
+FROM cw_State AS S
+WHERE NOT EXISTS(SELECT 1 FROM cw_CWUser AS X WHERE X.cw_in_state=S.cw_eid)'''),
]
OUTER_JOIN = [
@@ -1030,9 +1029,9 @@
WHERE N.cw_ecrit_par=P.cw_eid AND N.cw_eid=0'''),
('Any N WHERE NOT N ecrit_par P, P nom "toto"',
- '''SELECT DISTINCT N.cw_eid
+ '''SELECT N.cw_eid
FROM cw_Note AS N, cw_Personne AS P
-WHERE (N.cw_ecrit_par IS NULL OR N.cw_ecrit_par!=P.cw_eid) AND P.cw_nom=toto'''),
+WHERE NOT EXISTS(SELECT 1 WHERE N.cw_ecrit_par=P.cw_eid) AND P.cw_nom=toto'''),
('Any P WHERE N ecrit_par P, N eid 0',
'''SELECT N.cw_ecrit_par
@@ -1045,9 +1044,9 @@
WHERE N.cw_ecrit_par=P.cw_eid AND N.cw_eid=0'''),
('Any P WHERE NOT N ecrit_par P, P is Personne, N eid 512',
- '''SELECT DISTINCT P.cw_eid
+ '''SELECT P.cw_eid
FROM cw_Note AS N, cw_Personne AS P
-WHERE (N.cw_ecrit_par IS NULL OR N.cw_ecrit_par!=P.cw_eid) AND N.cw_eid=512'''),
+WHERE NOT EXISTS(SELECT 1 WHERE N.cw_ecrit_par=P.cw_eid) AND N.cw_eid=512'''),
('Any S,ES,T WHERE S state_of ET, ET name "CWUser", ES allowed_transition T, T destination_state S',
'''SELECT T.cw_destination_state, rel_allowed_transition1.eid_from, T.cw_eid
@@ -1070,23 +1069,23 @@
INTERSECT = [
('Any SN WHERE NOT X in_state S, S name SN',
- '''SELECT DISTINCT S.cw_name
-FROM cw_Affaire AS X, cw_State AS S
-WHERE (X.cw_in_state IS NULL OR X.cw_in_state!=S.cw_eid)
+ '''SELECT S.cw_name
+FROM cw_State AS S
+WHERE NOT EXISTS(SELECT 1 FROM cw_Affaire AS X WHERE X.cw_in_state=S.cw_eid)
INTERSECT
-SELECT DISTINCT S.cw_name
-FROM cw_CWUser AS X, cw_State AS S
-WHERE (X.cw_in_state IS NULL OR X.cw_in_state!=S.cw_eid)
+SELECT S.cw_name
+FROM cw_State AS S
+WHERE NOT EXISTS(SELECT 1 FROM cw_CWUser AS X WHERE X.cw_in_state=S.cw_eid)
INTERSECT
-SELECT DISTINCT S.cw_name
-FROM cw_Note AS X, cw_State AS S
-WHERE (X.cw_in_state IS NULL OR X.cw_in_state!=S.cw_eid)'''),
+SELECT S.cw_name
+FROM cw_State AS S
+WHERE NOT EXISTS(SELECT 1 FROM cw_Note AS X WHERE X.cw_in_state=S.cw_eid)'''),
('Any PN WHERE NOT X travaille S, X nom PN, S is IN(Division, Societe)',
'''SELECT X.cw_nom
FROM cw_Personne AS X
WHERE NOT EXISTS(SELECT 1 FROM travaille_relation AS rel_travaille0,cw_Division AS S WHERE rel_travaille0.eid_from=X.cw_eid AND rel_travaille0.eid_to=S.cw_eid)
-INTERSECT ALL
+INTERSECT
SELECT X.cw_nom
FROM cw_Personne AS X
WHERE NOT EXISTS(SELECT 1 FROM travaille_relation AS rel_travaille0,cw_Societe AS S WHERE rel_travaille0.eid_from=X.cw_eid AND rel_travaille0.eid_to=S.cw_eid)'''),
--- a/server/test/unittest_schemaserial.py Mon Jun 29 14:12:18 2009 +0200
+++ b/server/test/unittest_schemaserial.py Thu Jul 02 10:35:03 2009 +0200
@@ -94,7 +94,7 @@
('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is CWAttribute',
{'rt': 'cardinality', 'oe': 'String', 'ctname': u'SizeConstraint', 'se': 'CWAttribute', 'value': u'max=2'}),
('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is CWAttribute',
- {'rt': 'cardinality', 'oe': 'String', 'ctname': u'StaticVocabularyConstraint', 'se': 'CWAttribute', 'value': u"u'?1', u'11', u'??', u'1?'"}),
+ {'rt': 'cardinality', 'oe': 'String', 'ctname': u'StaticVocabularyConstraint', 'se': 'CWAttribute', 'value': u"u'?1', u'11'"}),
])
--- a/sobjects/supervising.py Mon Jun 29 14:12:18 2009 +0200
+++ b/sobjects/supervising.py Thu Jul 02 10:35:03 2009 +0200
@@ -48,7 +48,7 @@
events = ('before_delete_entity',)
def _call(self, eid):
- entity = self.session.entity(eid)
+ entity = self.session.entity_from_eid(eid)
try:
title = entity.dc_title()
except:
--- a/test/unittest_entity.py Mon Jun 29 14:12:18 2009 +0200
+++ b/test/unittest_entity.py Thu Jul 02 10:35:03 2009 +0200
@@ -296,6 +296,8 @@
self.assertEquals(e.printable_value('content'), e['content'])
e['content'] = u'été'
self.assertEquals(e.printable_value('content'), e['content'])
+ e['content'] = u'hop\r\nhop\nhip\rmomo'
+ self.assertEquals(e.printable_value('content'), u'hop\nhop\nhip\nmomo')
def test_fulltextindex(self):
--- a/view.py Mon Jun 29 14:12:18 2009 +0200
+++ b/view.py Thu Jul 02 10:35:03 2009 +0200
@@ -19,7 +19,7 @@
from cubicweb.selectors import require_group_compat, accepts_compat
from cubicweb.appobject import AppRsetObject
from cubicweb.utils import UStringIO, HTMLStream
-
+from cubicweb.schema import display_name
# robots control
NOINDEX = u'<meta name="ROBOTS" content="NOINDEX" />'
@@ -369,20 +369,20 @@
category = 'anyrsetview'
- def columns_labels(self, tr=True):
+ def columns_labels(self, mainindex=0, tr=True):
if tr:
- translate = display_name
+ translate = lambda val, req=self.req: display_name(req, val)
else:
- translate = lambda req, val: val
- rqlstdescr = self.rset.syntax_tree().get_description()[0] # XXX missing Union support
+ translate = lambda val: val
+ # XXX [0] because of missing Union support
+ rqlstdescr = self.rset.syntax_tree().get_description(mainindex,
+ translate)[0]
labels = []
- for colindex, attr in enumerate(rqlstdescr):
+ for colindex, label in enumerate(rqlstdescr):
# compute column header
- if colindex == 0 or attr == 'Any': # find a better label
- label = ','.join(translate(self.req, et)
+ if label == 'Any': # find a better label
+ label = ','.join(translate(et)
for et in self.rset.column_types(colindex))
- else:
- label = translate(self.req, attr)
labels.append(label)
return labels
--- a/web/controller.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/controller.py Thu Jul 02 10:35:03 2009 +0200
@@ -184,6 +184,12 @@
elif '__redirectpath' in self.req.form:
# if redirect path was explicitly specified in the form, use it
path = self.req.form['__redirectpath']
+ if self._edited_entity:
+ msg = newparams.get('__message', '')
+ msg += ' (<a href="%s">%s</a>)' % (
+ self._edited_entity.absolute_url(),
+ self.req._('click here to see created entity'))
+ newparams['__createdpath'] = self._edited_entity.rest_path()
elif self._after_deletion_path:
# else it should have been set during form processing
path, params = self._after_deletion_path
--- a/web/data/cubicweb.css Mon Jun 29 14:12:18 2009 +0200
+++ b/web/data/cubicweb.css Thu Jul 02 10:35:03 2009 +0200
@@ -445,16 +445,15 @@
}
div.sideBoxTitle {
- padding: 0.2em 0px;
background: #cfceb7;
display: block;
font: bold 100% Georgia;
+ padding : 2px 0;
}
div.sideBox {
- padding: 0.2em 0px;
+ padding: 0 0 0.2em;
margin-bottom: 0.5em;
- background: #eeedd9;
min-width: 21em;
max-width: 50em;
}
@@ -467,6 +466,7 @@
div.sideBoxBody {
padding: 0.2em 5px;
+ background: #eeedd9;
}
div.sideBoxBody a {
--- a/web/data/cubicweb.htmlhelpers.js Mon Jun 29 14:12:18 2009 +0200
+++ b/web/data/cubicweb.htmlhelpers.js Thu Jul 02 10:35:03 2009 +0200
@@ -254,7 +254,7 @@
//============= page loading events ==========================================//
function roundedCornersOnLoad() {
- jQuery('div.sideBox').corner('bottom 6px');
+ jQuery('div.sideBoxBody').corner('bottom 6px');
jQuery('div.boxTitle, div.boxPrefTitle, div.sideBoxTitle, th.month').corner('top 6px');
}
--- a/web/formfields.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/formfields.py Thu Jul 02 10:35:03 2009 +0200
@@ -164,7 +164,12 @@
widgets which desire it."""
if self.choices is not None:
if callable(self.choices):
- vocab = self.choices(req=form.req)
+ try:
+ vocab = self.choices(form=form)
+ except TypeError:
+ warn('vocabulary method (eg field.choices) should now take '
+ 'the form instance as argument', DeprecationWarning)
+ vocab = self.choices(req=form.req)
else:
vocab = self.choices
if vocab and not isinstance(vocab[0], (list, tuple)):
--- a/web/formwidgets.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/formwidgets.py Thu Jul 02 10:35:03 2009 +0200
@@ -231,6 +231,7 @@
"""
type = 'radio'
+
# javascript widgets ###########################################################
class DateTimePicker(TextInput):
--- a/web/request.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/request.py Thu Jul 02 10:35:03 2009 +0200
@@ -150,6 +150,11 @@
del self.form[k]
else:
self.form[k] = v
+ # special key for created entity, added in controller's reset method
+ if '__createdpath' in params:
+ self.message += ' (<a href="%s">%s</a>)' % (
+ self.build_url(params.pop('__createdpath')),
+ self._('click here to see created entity'))
def no_script_form_param(self, param, default=None, value=None):
"""ensure there is no script in a user form param
--- a/web/test/unittest_form.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/test/unittest_form.py Thu Jul 02 10:35:03 2009 +0200
@@ -5,6 +5,7 @@
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
+from __future__ import with_statement
from xml.etree.ElementTree import fromstring
@@ -98,6 +99,14 @@
inputs = pageinfo.find_tag('input', False)
self.failIf(any(attrs for t, attrs in inputs if attrs.get('name') == '__linkto'))
+ def test_reledit_composite_field(self):
+ rset = self.execute('INSERT BlogEntry X: X title "cubicweb.org", X content "hop"')
+ form = self.vreg.select_object('views', 'reledit', self.request(),
+ rset=rset, row=0, rtype='content')
+ data = form.render(row=0, rtype='content')
+ self.failUnless('edits-content' in data)
+ self.failUnless('edits-content_format' in data)
+
# form view tests #########################################################
def test_massmailing_formview(self):
--- a/web/test/unittest_views_baseviews.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/test/unittest_views_baseviews.py Thu Jul 02 10:35:03 2009 +0200
@@ -103,9 +103,9 @@
def test_sortvalue_with_display_col(self):
e, rset, view = self._prepare_entity()
- rqlstdescr = rset.syntax_tree().get_description()[0] # XXX missing Union support
+ labels = rset.column_labels()
table = TableWidget(view)
- table.columns = view.get_columns(rqlstdescr, [1, 2], None, None, None, None, 0)
+ table.columns = view.get_columns(labels, [1, 2], None, None, None, None, 0)
expected = ['loo"ong blabla'[:10], e.creation_date.strftime('%Y-%m-%d %H:%M')]
got = [loadjson(value) for _, value in table.itercols(0)]
self.assertListEqual(got, expected)
--- a/web/uicfg.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/uicfg.py Thu Jul 02 10:35:03 2009 +0200
@@ -129,6 +129,21 @@
self._counter += 1
tag.setdefault('order', self._counter)
+ def tag_subject_of(self, key, tag):
+ subj, rtype, obj = key
+ if obj != '*':
+ self.warning('using explict target type in display_ctrl.tag_subject_of() '
+ 'has no effect, use (%s, %s, "*") instead of (%s, %s, %s)',
+ subj, rtype, subj, rtype, obj)
+ super(DisplayCtrlRelationTags, self).tag_subject_of((subj, rtype, '*'), tag)
+
+ def tag_object_of(self, key, tag):
+ subj, rtype, obj = key
+ if subj != '*':
+ self.warning('using explict subject type in display_ctrl.tag_object_of() '
+ 'has no effect, use ("*", %s, %s) instead of (%s, %s, %s)',
+ rtype, obj, subj, rtype, obj)
+ super(DisplayCtrlRelationTags, self).tag_object_of(('*', rtype, obj), tag)
def init_primaryview_display_ctrl(rtag, sschema, rschema, oschema, role):
if role == 'subject':
--- a/web/views/__init__.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/views/__init__.py Thu Jul 02 10:35:03 2009 +0200
@@ -53,9 +53,15 @@
return True
return False
-VID_BY_MIMETYPE = {'text/xml': 'xml',
- # XXX rss, owl...
- }
+# FIXME: VID_BY_MIMETYPE is unfortunately a bit too naive since
+# some browsers (e.g. FF2) send a bunch of mimetypes in
+# the Accept header, for instance:
+# text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,
+# text/plain;q=0.8,image/png,*/*;q=0.5
+VID_BY_MIMETYPE = {
+ #'text/xml': 'xml',
+ # XXX rss, owl...
+}
def vid_from_rset(req, rset, schema):
"""given a result set, return a view id"""
if rset is None:
@@ -108,4 +114,7 @@
self._generate(tmpfile)
self.w(open(tmpfile).read())
finally:
- os.unlink(tmpfile)
+ try:
+ os.unlink(tmpfile)
+ except Exception, ex:
+ self.warning('cant delete %s: %s', tmpfile, ex)
--- a/web/views/basecomponents.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/views/basecomponents.py Thu Jul 02 10:35:03 2009 +0200
@@ -110,11 +110,6 @@
self.w(self.req._('anonymous'))
self.w(u''' [<a class="logout" href="javascript: popupLoginBox();">%s</a>]'''
% (self.req._('i18n_login_popup')))
- # FIXME maybe have an other option to explicitely authorise registration
- # also provide a working register view
-# if self.config['anonymous-user']:
-# self.w(u''' [<a class="logout" href="?vid=register">%s</a>]'''
-# % (self.req._('i18n_register_user')))
else:
self.w(self.req._('anonymous'))
self.w(u' [<a class="logout" href="%s">%s</a>]'
--- a/web/views/cwproperties.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/views/cwproperties.py Thu Jul 02 10:35:03 2009 +0200
@@ -299,11 +299,10 @@
_ = form.req._
if entity.has_eid():
return [(_(entity.pkey), entity.pkey)]
- # key beginning with 'system.' should usually not be edited by hand
choices = entity.vreg.user_property_keys()
return [(u'', u'')] + sorted(zip((_(v) for v in choices), choices))
-
+
class PropertyValueField(StringField):
"""specific field for CWProperty.value which will be different according to
the selected key type and vocabulary information
--- a/web/views/editforms.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/views/editforms.py Thu Jul 02 10:35:03 2009 +0200
@@ -102,17 +102,25 @@
if not default:
default = self.req._('not specified')
if rschema.is_final():
- if getattr(entity, rtype) is None:
- value = default
- else:
- value = entity.printable_value(rtype)
+ value = entity.printable_value(rtype)
+ if not entity.has_perm('update'):
+ self.w(value)
+ return
else:
rset = entity.related(rtype, role)
# XXX html_escape but that depends of the actual vid
value = html_escape(self.view(vid, rset, 'null') or default)
- if not entity.has_perm('update'):
+ # XXX consider local roles ?
+ if role == 'subject'and not rschema.has_perm(self.req, 'add',
+ fromeid=entity.eid):
self.w(value)
return
+ elif role == 'object'and not rschema.has_perm(self.req, 'add',
+ toeid=entity.eid):
+ self.w(value)
+ return
+ if not value.strip():
+ value = default
if rschema.is_final():
form = self._build_attribute_form(entity, value, rtype, role,
reload, row, col, default)
--- a/web/views/facets.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/views/facets.py Thu Jul 02 10:35:03 2009 +0200
@@ -38,6 +38,9 @@
order = 1
roundcorners = True
+ needs_css = 'cubicweb.facets.css'
+ needs_js = ('cubicweb.ajax.js', 'cubicweb.formfilter.js')
+
def facetargs(self):
"""this method returns the list of extra arguments that should
be used by the facet
@@ -56,8 +59,8 @@
def call(self, view=None):
req = self.req
- req.add_js( ('cubicweb.ajax.js', 'cubicweb.formfilter.js') )
- req.add_css('cubicweb.facets.css')
+ req.add_js( self.needs_js )
+ req.add_css( self.needs_css)
if self.roundcorners:
req.html_headers.add_onload('jQuery(".facet").corner("tl br 10px");')
rset, vid, divid, paginate = self._get_context(view)
--- a/web/views/formrenderers.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/views/formrenderers.py Thu Jul 02 10:35:03 2009 +0200
@@ -215,12 +215,29 @@
class BaseFormRenderer(FormRenderer):
- """use form_renderer_id = 'base' if you want base FormRenderer without
- adaptation by selection
+ """use form_renderer_id = 'base' if you want base FormRenderer layout even
+ when selected for an entity
"""
id = 'base'
+class EntityBaseFormRenderer(BaseFormRenderer):
+ """use form_renderer_id = 'base' if you want base FormRenderer layout even
+ when selected for an entity
+ """
+ __select__ = entity_implements('Any')
+
+ def display_field(self, form, field):
+ if not super(EntityBaseFormRenderer, self).display_field(form, field):
+ if isinstance(field, HiddenInitialValueField):
+ field = field.visible_field
+ ismeta = form.edited_entity.e_schema.is_metadata(field.name)
+ return ismeta is not None and (
+ ismeta[0] in self.display_fields or
+ (ismeta[0], 'subject') in self.display_fields)
+ return True
+
+
class HTableFormRenderer(FormRenderer):
"""display fields horizontally in a table
@@ -310,9 +327,11 @@
w(u'</tr>')
-class EntityFormRenderer(FormRenderer):
+class EntityFormRenderer(EntityBaseFormRenderer):
"""specific renderer for entity edition form (edition)"""
- __select__ = entity_implements('Any') & yes()
+ id = 'default'
+ # needs some additional points in some case (XXX explain cases)
+ __select__ = EntityBaseFormRenderer.__select__ & yes()
_options = FormRenderer._options + ('display_relations_form',)
display_relations_form = True
--- a/web/views/primary.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/views/primary.py Thu Jul 02 10:35:03 2009 +0200
@@ -14,10 +14,10 @@
from cubicweb import Unauthorized
from cubicweb.view import EntityView
+from cubicweb.schema import display_name
from cubicweb.web import uicfg
-
class PrimaryView(EntityView):
"""the full view of an non final entity"""
id = 'primary'
@@ -119,7 +119,19 @@
def render_entity_attributes(self, entity, siderelations=None):
for rschema, tschemas, role, dispctrl in self._section_def(entity, 'attributes'):
- vid = dispctrl.get('vid', 'reledit')
+ # don't use reledit as default vid for composite relation
+ if rschema.is_final():
+ defaultvid = 'reledit'
+ # XXX use entity.e_schema.role_rproperty(role, rschema, 'composite', tschemas[0]) once yams > 0.23.0 is out
+ elif role == 'subject' and \
+ rschema.rproperty(entity.e_schema, tschemas[0], 'composite'):
+ defaultvid = 'csv'
+ elif role == 'object' and \
+ rschema.rproperty(tschemas[0], entity.e_schema, 'composite'):
+ defaultvid = 'csv'
+ else:
+ defaultvid = 'reledit'
+ vid = dispctrl.get('vid', defaultvid)
if rschema.is_final() or vid == 'reledit':
value = entity.view(vid, rtype=rschema.type, role=role)
else:
--- a/web/views/schema.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/views/schema.py Thu Jul 02 10:35:03 2009 +0200
@@ -49,6 +49,7 @@
% (entity.dc_type().capitalize(),
html_escape(entity.dc_long_title())))
+
# CWEType ######################################################################
class CWETypeOneLineView(baseviews.OneLineView):
@@ -85,11 +86,10 @@
def cell_call(self, row, col):
entity = self.entity(row, col)
self.w(u'<h2>%s</h2>' % _('Attributes'))
- rset = self.req.execute('Any N,F,D,GROUP_CONCAT(C),I,J,DE,A '
- 'GROUPBY N,F,D,AA,A,I,J,DE '
+ rset = self.req.execute('Any N,F,D,I,J,DE,A '
'ORDERBY AA WHERE A is CWAttribute, '
'A ordernum AA, A defaultval D, '
- 'A constrained_by C?, A description DE, '
+ 'A description DE, '
'A fulltextindexed I, A internationalizable J, '
'A relation_type R, R name N, '
'A to_entity O, O name F, '
@@ -97,25 +97,22 @@
{'x': entity.eid})
self.wview('editable-table', rset, 'null', displayfilter=True)
self.w(u'<h2>%s</h2>' % _('Relations'))
- rset = self.req.execute('Any N,C,F,M,K,D,A ORDERBY N '
- 'WITH N,C,F,M,D,K,A BEING ('
- '(Any N,C,F,M,K,D,A '
- 'ORDERBY N WHERE A is CWRelation, '
- 'A description D, A composite K?, '
- 'A relation_type R, R name N, '
- 'A to_entity O, O name F, '
- 'A cardinality C, O meta M, '
- 'A from_entity S, S eid %(x)s)'
- ' UNION '
- '(Any N,C,F,M,K,D,A '
- 'ORDERBY N WHERE A is CWRelation, '
- 'A description D, A composite K?, '
- 'A relation_type R, R name N, '
- 'A from_entity S, S name F, '
- 'A cardinality C, S meta M, '
- 'A to_entity O, O eid %(x)s))'
- ,{'x': entity.eid})
- self.wview('editable-table', rset, 'null', displayfilter=True)
+ rset = self.req.execute(
+ 'Any R,C,TT,K,D,A,RN,TTN ORDERBY RN '
+ 'WHERE A is CWRelation, A description D, A composite K?, '
+ 'A relation_type R, R name RN, A to_entity TT, TT name TTN, '
+ 'A cardinality C, A from_entity S, S eid %(x)s',
+ {'x': entity.eid})
+ self.wview('editable-table', rset, 'null', displayfilter=True,
+ displaycols=range(6), mainindex=5)
+ rset = self.req.execute(
+ 'Any R,C,TT,K,D,A,RN,TTN ORDERBY RN '
+ 'WHERE A is CWRelation, A description D, A composite K?, '
+ 'A relation_type R, R name RN, A from_entity TT, TT name TTN, '
+ 'A cardinality C, A to_entity O, O eid %(x)s',
+ {'x': entity.eid})
+ self.wview('editable-table', rset, 'null', displayfilter=True,
+ displaycols=range(6), mainindex=5)
class CWETypeSImageView(EntityView):
--- a/web/views/startup.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/views/startup.py Thu Jul 02 10:35:03 2009 +0200
@@ -194,15 +194,9 @@
id = 'schema-text'
def call(self):
- self.w(u'<p>%s</p>' % _('This is the list of types defined in the data '
- 'model ofin this application.'))
- self.w(u'<p>%s</p>' % _('<em>meta</em> is True for types that are defined by the '
- 'framework itself (e.g. User and Group). '
- '<em>final</em> is True for types that can not be the '
- 'subject of a relation (e.g. Int and String).'))
- rset = self.req.execute('Any X,M,F ORDERBY N WHERE X is CWEType, X name N, '
- 'X meta M, X final F')
- self.wview('editable-table', rset, displayfilter=True)
+ rset = self.req.execute('Any X ORDERBY N WHERE X is CWEType, X name N, '
+ 'X final FALSE')
+ self.wview('table', rset, displayfilter=True)
class ManagerSchemaPermissionsView(StartupView, management.SecurityViewMixIn):
--- a/web/views/tableview.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/views/tableview.py Thu Jul 02 10:35:03 2009 +0200
@@ -93,7 +93,7 @@
def call(self, title=None, subvid=None, displayfilter=None, headers=None,
displaycols=None, displayactions=None, actions=(), divid=None,
- cellvids=None, cellattrs=None):
+ cellvids=None, cellattrs=None, mainindex=None):
"""Dumps a table displaying a composite query
:param title: title added before table
@@ -101,19 +101,18 @@
:param displayfilter: filter that selects rows to display
:param headers: columns' titles
"""
- rset = self.rset
req = self.req
req.add_js('jquery.tablesorter.js')
req.add_css(('cubicweb.tablesorter.css', 'cubicweb.tableview.css'))
- rqlst = rset.syntax_tree()
- # get rql description first since the filter form may remove some
- # necessary information
- rqlstdescr = rqlst.get_description()[0] # XXX missing Union support
- mainindex = self.main_var_index()
+ # compute label first since the filter form may remove some necessary
+ # information from the rql syntax tree
+ if mainindex is None:
+ mainindex = self.main_var_index()
+ computed_labels = self.columns_labels(mainindex)
hidden = True
if not subvid and 'subvid' in req.form:
subvid = req.form.pop('subvid')
- divid = divid or req.form.get('divid') or 'rs%s' % make_uid(id(rset))
+ divid = divid or req.form.get('divid') or 'rs%s' % make_uid(id(self.rset))
actions = list(actions)
if mainindex is None:
displayfilter, displayactions = False, False
@@ -154,8 +153,8 @@
self.render_actions(divid, actions)
# render table
table = TableWidget(self)
- for column in self.get_columns(rqlstdescr, displaycols, headers, subvid,
- cellvids, cellattrs, mainindex):
+ for column in self.get_columns(computed_labels, displaycols, headers,
+ subvid, cellvids, cellattrs, mainindex):
table.append_column(column)
table.render(self.w)
self.w(u'</div>\n')
@@ -188,20 +187,15 @@
box.render(w=self.w)
self.w(u'<div class="clear"/>')
- def get_columns(self, rqlstdescr, displaycols, headers, subvid, cellvids,
- cellattrs, mainindex):
+ def get_columns(self, computed_labels, displaycols, headers, subvid,
+ cellvids, cellattrs, mainindex):
columns = []
- for colindex, attr in enumerate(rqlstdescr):
+ for colindex, label in enumerate(computed_labels):
if colindex not in displaycols:
continue
# compute column header
if headers is not None:
label = headers[displaycols.index(colindex)]
- elif colindex == 0 or attr == 'Any': # find a better label
- label = ','.join(display_name(self.req, et)
- for et in self.rset.column_types(colindex))
- else:
- label = display_name(self.req, attr)
if colindex == mainindex:
label += ' (%s)' % self.rset.rowcount
column = TableColumn(label, colindex)
@@ -214,7 +208,6 @@
column.append_renderer(self.finalview, colindex)
else:
column.append_renderer(subvid or 'incontext', colindex)
-
if cellattrs and colindex in cellattrs:
for name, value in cellattrs[colindex].iteritems():
column.add_attr(name, value)
@@ -297,7 +290,7 @@
title = None
def call(self, title=None, subvid=None, headers=None, divid=None,
- displaycols=None, displayactions=None):
+ displaycols=None, displayactions=None, mainindex=None):
"""Dumps a table displaying a composite query"""
actrql = self.req.form['actualrql']
self.ensure_ro_rql(actrql)
@@ -312,7 +305,8 @@
title = self.req.form.pop('title')
if title:
self.w(u'<h2>%s</h2>\n' % title)
- mainindex = self.main_var_index()
+ if mainindex is None:
+ mainindex = self.main_var_index()
if mainindex is not None:
actions = self.form_filter(divid, displaycols, displayactions, True)
else:
--- a/web/views/treeview.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/views/treeview.py Thu Jul 02 10:35:03 2009 +0200
@@ -106,7 +106,8 @@
liclasses = []
is_last = row == len(self.rset) - 1
is_open = self.open_state(entity.eid, treeid)
- if not hasattr(entity, 'is_leaf') or entity.is_leaf():
+ is_leaf = not hasattr(entity, 'is_leaf') or entity.is_leaf()
+ if is_leaf:
if is_last:
liclasses.append('last')
w(u'<li class="%s">' % u' '.join(liclasses))
@@ -145,7 +146,7 @@
w(u'<ul class="placeholder"><li>place holder</li></ul>')
# the local node info
self.wview(vid, self.rset, row=row, col=col)
- if is_open: # => not leaf => rql is defined
+ if is_open and not is_leaf: # => rql is defined
self.wview(parentvid, self.req.execute(rql), treeid=treeid, initial_load=False)
w(u'</li>')
--- a/web/views/xmlrss.py Mon Jun 29 14:12:18 2009 +0200
+++ b/web/views/xmlrss.py Thu Jul 02 10:35:03 2009 +0200
@@ -77,7 +77,7 @@
w = self.w
rset, descr = self.rset, self.rset.description
eschema = self.schema.eschema
- labels = self.columns_labels(False)
+ labels = self.columns_labels(tr=False)
w(u'<?xml version="1.0" encoding="%s"?>\n' % self.req.encoding)
w(u'<%s query="%s">\n' % (self.xml_root, xml_escape(rset.printable_rql())))
for rowindex, row in enumerate(self.rset):