# HG changeset patch # User Sylvain Thénault # Date 1255595514 -7200 # Node ID 387d51af966dd34d9ba8fd3370439ed0cdb19496 # Parent 12ea53a4c0da948e56bb6ea47fe6727fdb38e1c0# Parent 9342e6783bd2dedc8dfe61d77493c9893535807e backport stable branch diff -r 12ea53a4c0da -r 387d51af966d __init__.py --- a/__init__.py Tue Oct 13 18:21:24 2009 +0200 +++ b/__init__.py Thu Oct 15 10:31:54 2009 +0200 @@ -21,6 +21,7 @@ from logilab.common.logging_ext import set_log_methods + if os.environ.get('APYCOT_ROOT'): logging.basicConfig(level=logging.CRITICAL) else: @@ -71,7 +72,6 @@ } - # XXX cubic web cube migration map CW_MIGRATION_MAP = {'erudi': 'cubicweb', 'eaddressbook': 'addressbook', diff -r 12ea53a4c0da -r 387d51af966d cwconfig.py --- a/cwconfig.py Tue Oct 13 18:21:24 2009 +0200 +++ b/cwconfig.py Thu Oct 15 10:31:54 2009 +0200 @@ -5,10 +5,71 @@ :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr -.. envvar:: CW_CUBES_PATH + +If cubicweb is a mercurial checkout (eg `CWDEV` is true), located in +`CW_SOFTWARE_ROOT`: + + * main cubes directory is `/../cubes`. You can specify + another one with `CW_INSTANCES_DIR` environment variable or simply add some + other directories by using `CW_CUBES_PATH`. + + * cubicweb migration files are by default searched in + `/misc/migration` instead of + `/usr/share/cubicweb/migration/`(unless another emplacement is specified + using `CW_MIGRATION_DIR`. + + * Cubicweb will start in 'user' mode (see below) + + +On startup, Cubicweb is using a specific *mode*. A mode corresponds to some +default setting for various resource directories. There are currently 2 main +modes : 'system', for system wide installation, and 'user', fur user local +installation (e.g. no root privileges). + +'user' mode is activated automatically when cubicweb is a mercurial checkout +(e.g. has a .hg directory). You can also force mode by using the `CW_MODE` +environment variable, to: + +* use system wide installation but user specific instances and all, without root + privileges on the system (`export CW_MODE=user`) + +* use local checkout of cubicweb on system wide instances (requires root + privileges on the system (`export CW_MODE=system`) + Here is the default resource directories settings according to mode: + +* 'system': :: + + CW_INSTANCES_DIR = /etc/cubicweb.d/ + CW_INSTANCES_DATA_DIR = /var/lib/cubicweb/instances/ + CW_RUNTIME_DIR = /var/run/cubicweb/ + + * 'user': :: + + CW_INSTANCES_DIR = ~/etc/cubicweb.d/ + CW_INSTANCES_DATA_DIR = ~/etc/cubicweb.d/ + CW_RUNTIME_DIR = /tmp + + +.. envvar:: CW_MODE + Resource mode: user or system + +.. envvar:: CW_CUBES_PATH Augments the default search path for cubes +.. envvar:: CW_INSTANCES_DIR + Directory where cubicweb instances will be found + +.. envvar:: CW_INSTANCES_DATA_DIR + Directory where cubicweb instances data will be written + +.. envvar:: CW_RUNTIME_DIR + Directory where pid files will be written + +.. envvar:: CW_MIGRATION_DIR + Directory where cubicweb migration files will be found + + :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses """ __docformat__ = "restructuredtext en" @@ -138,6 +199,8 @@ _forced_mode = os.environ.get('CW_MODE') assert _forced_mode in (None, 'system', 'user') +CWDEV = exists(join(CW_SOFTWARE_ROOT, '.hg')) + class CubicWebNoAppConfiguration(ConfigurationMixIn): """base class for cubicweb configuration without a specific instance directory """ @@ -148,17 +211,22 @@ log_format = '%(asctime)s - (%(name)s) %(levelname)s: %(message)s' # nor remove appobjects based on unused interface cleanup_interface_sobjects = True + # debug mode + debugmode = False if os.environ.get('APYCOT_ROOT'): mode = 'test' CUBES_DIR = '%(APYCOT_ROOT)s/local/share/cubicweb/cubes/' % os.environ # create __init__ file file(join(CUBES_DIR, '__init__.py'), 'w').close() - elif (exists(join(CW_SOFTWARE_ROOT, '.hg')) and _forced_mode != 'system') or _forced_mode == 'user': - mode = 'dev' + elif (CWDEV and _forced_mode != 'system'): + mode = 'user' CUBES_DIR = abspath(normpath(join(CW_SOFTWARE_ROOT, '../cubes'))) else: - mode = 'installed' + if _forced_mode == 'user': + mode = 'user' + else: + mode = 'system' CUBES_DIR = '/usr/share/cubicweb/cubes/' options = ( @@ -245,14 +313,14 @@ """return the shared data directory (i.e. directory where standard library views and data may be found) """ - if cls.mode in ('dev', 'test') and not os.environ.get('APYCOT_ROOT'): + if CWDEV: return join(CW_SOFTWARE_ROOT, 'web') return cls.cube_dir('shared') @classmethod def i18n_lib_dir(cls): """return instance's i18n directory""" - if cls.mode in ('dev', 'test') and not os.environ.get('APYCOT_ROOT'): + if CWDEV: return join(CW_SOFTWARE_ROOT, 'i18n') return join(cls.shared_dir(), 'i18n') @@ -261,7 +329,7 @@ cubes = set() for directory in cls.cubes_search_path(): for cube in os.listdir(directory): - if isdir(join(directory, cube)) and not cube in ('CVS', '.svn', 'shared', '.hg'): + if isdir(join(directory, cube)) and not cube == 'shared': cubes.add(cube) return sorted(cubes) @@ -497,6 +565,7 @@ logthreshold = 'DEBUG' else: logthreshold = self['log-threshold'] + self.debugmode = debug init_log(debug, syslog, logthreshold, logfile, self.log_format) # configure simpleTal logger logging.getLogger('simpleTAL').setLevel(logging.ERROR) @@ -538,7 +607,7 @@ class CubicWebConfiguration(CubicWebNoAppConfiguration): """base class for cubicweb server and web configurations""" - INSTANCE_DATA_DIR = None + INSTANCES_DATA_DIR = None if CubicWebNoAppConfiguration.mode == 'test': root = os.environ['APYCOT_ROOT'] REGISTRY_DIR = '%s/etc/cubicweb.d/' % root @@ -546,15 +615,19 @@ MIGRATION_DIR = '%s/local/share/cubicweb/migration/' % root if not exists(REGISTRY_DIR): os.makedirs(REGISTRY_DIR) - elif CubicWebNoAppConfiguration.mode == 'dev': - REGISTRY_DIR = expanduser('~/etc/cubicweb.d/') - RUNTIME_DIR = tempfile.gettempdir() - MIGRATION_DIR = join(CW_SOFTWARE_ROOT, 'misc', 'migration') - else: #mode = 'installed' - REGISTRY_DIR = '/etc/cubicweb.d/' - INSTANCE_DATA_DIR = '/var/lib/cubicweb/instances/' - RUNTIME_DIR = '/var/run/cubicweb/' - MIGRATION_DIR = '/usr/share/cubicweb/migration/' + else: + if CubicWebNoAppConfiguration.mode == 'user': + REGISTRY_DIR = expanduser('~/etc/cubicweb.d/') + RUNTIME_DIR = tempfile.gettempdir() + INSTANCES_DATA_DIR = REGISTRY_DIR + else: #mode = 'system' + REGISTRY_DIR = '/etc/cubicweb.d/' + RUNTIME_DIR = '/var/run/cubicweb/' + INSTANCES_DATA_DIR = '/var/lib/cubicweb/instances/' + if CWDEV: + MIGRATION_DIR = join(CW_SOFTWARE_ROOT, 'misc', 'migration') + else: + MIGRATION_DIR = '/usr/share/cubicweb/migration/' # for some commands (creation...) we don't want to initialize gettext set_language = True @@ -612,8 +685,7 @@ @classmethod def instance_data_dir(cls): """return the instance data directory""" - return env_path('CW_INSTANCES_DATA_DIR', - cls.INSTANCE_DATA_DIR or cls.REGISTRY_DIR, + return env_path('CW_INSTANCES_DATA_DIR', cls.INSTANCES_DATA_DIR, 'additional data') @classmethod @@ -666,7 +738,7 @@ def default_log_file(self): """return default path to the log file of the instance'server""" - if self.mode == 'dev': + if self.mode == 'user': basepath = join(tempfile.gettempdir(), '%s-%s' % (basename(self.appid), self.name)) path = basepath + '.log' i = 1 diff -r 12ea53a4c0da -r 387d51af966d cwctl.py --- a/cwctl.py Tue Oct 13 18:21:24 2009 +0200 +++ b/cwctl.py Thu Oct 15 10:31:54 2009 +0200 @@ -18,10 +18,9 @@ from logilab.common.shellutils import ASK from cubicweb import ConfigurationError, ExecutionError, BadCommandUsage -from cubicweb.cwconfig import CubicWebConfiguration as cwcfg, CONFIGURATIONS +from cubicweb.cwconfig import CubicWebConfiguration as cwcfg, CWDEV, CONFIGURATIONS from cubicweb.toolsutils import Command, main_run, rm, create_dir, underline_title - def wait_process_end(pid, maxtry=10, waittime=1): """wait for a process to actually die""" import signal @@ -657,7 +656,7 @@ for cube, fromversion, toversion in toupgrade: print '-> migration needed from %s to %s for %s' % (fromversion, toversion, cube) # only stop once we're sure we have something to do - if not (cwcfg.mode == 'dev' or self.config.nostartstop): + if not (CWDEV or self.config.nostartstop): StopInstanceCommand().stop_instance(appid) # run cubicweb/componants migration scripts mih.migrate(vcconf, reversed(toupgrade), self.config) @@ -680,7 +679,7 @@ mih.shutdown() print print '-> instance migrated.' - if not (cwcfg.mode == 'dev' or self.config.nostartstop): + if not (CWDEV or self.config.nostartstop): StartInstanceCommand().start_instance(appid) print diff -r 12ea53a4c0da -r 387d51af966d cwvreg.py --- a/cwvreg.py Tue Oct 13 18:21:24 2009 +0200 +++ b/cwvreg.py Thu Oct 15 10:31:54 2009 +0200 @@ -344,7 +344,7 @@ def register_objects(self, path, force_reload=None): """overriden to remove objects requiring a missing interface""" if force_reload is None: - force_reload = self.config.mode == 'dev' + force_reload = self.config.debugmode try: self._register_objects(path, force_reload) except RegistryOutOfDate: diff -r 12ea53a4c0da -r 387d51af966d devtools/devctl.py --- a/devtools/devctl.py Tue Oct 13 18:21:24 2009 +0200 +++ b/devtools/devctl.py Thu Oct 15 10:31:54 2009 +0200 @@ -492,8 +492,6 @@ if len(args) != 1: raise BadCommandUsage("exactly one argument (cube name) is expected") cubename, = args - #if ServerConfiguration.mode != "dev": - # self.fail("you can only create new cubes in development mode") verbose = self.get('verbose') cubesdir = self.get('directory') if not cubesdir: diff -r 12ea53a4c0da -r 387d51af966d devtools/fill.py --- a/devtools/fill.py Tue Oct 13 18:21:24 2009 +0200 +++ b/devtools/fill.py Thu Oct 15 10:31:54 2009 +0200 @@ -404,12 +404,14 @@ for subjeid, objeid in used: if subjcard in '?1' and subjeid in subjeids: subjeids.remove(subjeid) - if objeid in objeids: - objeids.remove(objeid) + # XXX why? + #if objeid in objeids: + # objeids.remove(objeid) if objcard in '?1' and objeid in objeids: objeids.remove(objeid) - if subjeid in subjeids: - subjeids.remove(subjeid) + # XXX why? + #if subjeid in subjeids: + # subjeids.remove(subjeid) if not subjeids: check_card_satisfied(objcard, objeids, subj, rschema, obj) return diff -r 12ea53a4c0da -r 387d51af966d doc/book/en/development/testing/index.rst --- a/doc/book/en/development/testing/index.rst Tue Oct 13 18:21:24 2009 +0200 +++ b/doc/book/en/development/testing/index.rst Thu Oct 15 10:31:54 2009 +0200 @@ -56,7 +56,7 @@ # the default admin connection and one may be tempted to close it self.restore_connection() -Take care of the references kept to the entities created with a connection or the other. +Do not use the references kept to the entities created with a connection from another. Email notifications tests diff -r 12ea53a4c0da -r 387d51af966d entities/wfobjs.py --- a/entities/wfobjs.py Tue Oct 13 18:21:24 2009 +0200 +++ b/entities/wfobjs.py Thu Oct 15 10:31:54 2009 +0200 @@ -417,17 +417,11 @@ def cwetype_workflow(self): """return the default workflow for entities of this type""" # XXX CWEType method - wfrset = self._cw.execute('Any WF WHERE X is ET, X eid %(x)s, ' - 'WF workflow_of ET', {'x': self.eid}, 'x') - if len(wfrset) == 1: + wfrset = self._cw.execute('Any WF WHERE ET default_workflow WF, ' + 'ET name %(et)s', {'et': self.id}) + if wfrset: return wfrset.get_entity(0, 0) - if len(wfrset) > 1: - for wf in wfrset.entities(): - if wf.is_default_workflow_of(self.__regid__): - return wf - self.warning("can't find default workflow for %s", self.__regid__) - else: - self.warning("can't find any workflow for %s", self.__regid__) + self.warning("can't find any workflow for %s", self.id) return None def possible_transitions(self, type='normal'): diff -r 12ea53a4c0da -r 387d51af966d entity.py --- a/entity.py Tue Oct 13 18:21:24 2009 +0200 +++ b/entity.py Thu Oct 15 10:31:54 2009 +0200 @@ -18,7 +18,7 @@ from rql import parse from rql.utils import rqlvar_maker -from cubicweb import Unauthorized +from cubicweb import Unauthorized, typed_eid from cubicweb.rset import ResultSet from cubicweb.selectors import yes from cubicweb.appobject import AppObject @@ -232,7 +232,7 @@ meaning that the entity has to be created """ try: - int(self.eid) + typed_eid(self.eid) return True except (ValueError, TypeError): return False @@ -587,14 +587,18 @@ def related_rql(self, rtype, role='subject', targettypes=None): rschema = self._cw.vreg.schema[rtype] if role == 'subject': + restriction = 'E eid %%(x)s, E %s X' % rtype if targettypes is None: targettypes = rschema.objects(self.e_schema) - restriction = 'E eid %%(x)s, E %s X' % rtype + else: + restriction += 'E is IN (%s)' % ','.join(targettypes) card = greater_card(rschema, (self.e_schema,), targettypes, 0) else: + restriction = 'E eid %%(x)s, X %s E' % rtype if targettypes is None: targettypes = rschema.subjects(self.e_schema) - restriction = 'E eid %%(x)s, X %s E' % rtype + else: + restriction += 'E is IN (%s)' % ','.join(targettypes) card = greater_card(rschema, targettypes, (self.e_schema,), 1) if len(targettypes) > 1: fetchattrs_list = [] @@ -741,9 +745,14 @@ self._related_cache.pop('%s_%s' % (rtype, role), None) def clear_all_caches(self): + haseid = 'eid' in self self.clear() for rschema, _, role in self.e_schema.relation_definitions(): self.clear_related_cache(rschema.type, role) + # set eid if it was in, else we may get nasty error while editing this + # entity if it's bound to a repo session + if haseid: + self['eid'] = self.eid # raw edition utilities ################################################### @@ -763,6 +772,20 @@ self._cw.execute('SET %s WHERE X eid %%(x)s' % ','.join(relations), kwargs, 'x') + def set_relations(self, _cw_unsafe=False, **kwargs): + if _cw_unsafe: + execute = self.req.unsafe_execute + else: + execute = self.req.execute + for attr, values in kwargs.iteritems(): + if attr.startswith('reverse_'): + restr = 'Y %s X' % attr[len('reverse_'):] + else: + restr = 'X %s Y' % attr + execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % ( + restr, ','.join(str(r.eid) for r in values)), + {'x': self.eid}, 'x') + def delete(self): assert self.has_eid(), self.eid self._cw.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema, diff -r 12ea53a4c0da -r 387d51af966d etwist/server.py --- a/etwist/server.py Tue Oct 13 18:21:24 2009 +0200 +++ b/etwist/server.py Thu Oct 15 10:31:54 2009 +0200 @@ -180,8 +180,8 @@ def render(self, request): """Render a page from the root resource""" - # reload modified files (only in development or debug mode) - if self.config.mode == 'dev' or self.debugmode: + # reload modified files in debug mode + if self.debugmode: self.appli.vreg.register_objects(self.config.vregistry_path()) if self.config['profile']: # default profiler don't trace threads return self.render_request(request) diff -r 12ea53a4c0da -r 387d51af966d etwist/twconfig.py --- a/etwist/twconfig.py Tue Oct 13 18:21:24 2009 +0200 +++ b/etwist/twconfig.py Thu Oct 15 10:31:54 2009 +0200 @@ -47,7 +47,7 @@ 'default': None, 'help': 'if this option is set, use the specified user to start \ the repository rather than the user running the command', - 'group': 'main', 'inputlevel': (WebConfiguration.mode == 'installed') and 0 or 1, + 'group': 'main', 'inputlevel': WebConfiguration.mode == 'system' }), ('session-time', {'type' : 'int', diff -r 12ea53a4c0da -r 387d51af966d hooks/workflow.py --- a/hooks/workflow.py Tue Oct 13 18:21:24 2009 +0200 +++ b/hooks/workflow.py Thu Oct 15 10:31:54 2009 +0200 @@ -106,6 +106,34 @@ outputs.add(ep.subwf_state.eid) +class _SubWorkflowExitOp(PreCommitOperation): + + def precommit_event(self): + session = self.session + forentity = self.forentity + trinfo = self.trinfo + # we're in a subworkflow, check if we've reached an exit point + wftr = forentity.subworkflow_input_transition() + if wftr is None: + # inconsistency detected + msg = session._("state doesn't belong to entity's current workflow") + raise ValidationError(self.trinfo.eid, {'to_state': msg}) + tostate = wftr.get_exit_point(forentity, trinfo['to_state']) + if tostate is not None: + # reached an exit point + msg = session._('exiting from subworkflow %s') + msg %= session._(forentity.current_workflow.name) + session.transaction_data[(forentity.eid, 'subwfentrytr')] = True + # XXX iirk + req = forentity.req + forentity.req = session.super_session + try: + trinfo = forentity.change_state(tostate, msg, u'text/plain', + tr=wftr) + finally: + forentity.req = req + + # hooks ######################################################################## class WorkflowHook(hook.Hook): @@ -229,33 +257,13 @@ events = ('after_add_entity',) def __call__(self): - session = self._cw - entity = self.entity - _change_state(session, entity['wf_info_for'], - entity['from_state'], entity['to_state']) - forentity = session.entity_from_eid(entity['wf_info_for']) - assert forentity.current_state.eid == entity['to_state'] + trinfo = self.entity + _change_state(self._cw, trinfo['wf_info_for'], + trinfo['from_state'], trinfo['to_state']) + forentity = self._cw.entity_from_eid(trinfo['wf_info_for']) + assert forentity.current_state.eid == trinfo['to_state'] if forentity.main_workflow.eid != forentity.current_workflow.eid: - # we're in a subworkflow, check if we've reached an exit point - wftr = forentity.subworkflow_input_transition() - if wftr is None: - # inconsistency detected - msg = session._("state doesn't belong to entity's current workflow") - raise ValidationError(entity.eid, {'to_state': msg}) - tostate = wftr.get_exit_point(forentity, entity['to_state']) - if tostate is not None: - # reached an exit point - msg = session._('exiting from subworkflow %s') - msg %= session._(forentity.current_workflow.name) - session.transaction_data[(forentity.eid, 'subwfentrytr')] = True - # XXX iirk - req = forentity._cw - forentity._cw = session.super_session - try: - trinfo = forentity.change_state(tostate, msg, u'text/plain', - tr=wftr) - finally: - forentity._cw = req + _SubWorkflowExitOp(self._cw, forentity=forentity, trinfo=trinfo) class CheckInStateChangeAllowed(WorkflowHook): diff -r 12ea53a4c0da -r 387d51af966d interfaces.py --- a/interfaces.py Tue Oct 13 18:21:24 2009 +0200 +++ b/interfaces.py Thu Oct 15 10:31:54 2009 +0200 @@ -127,7 +127,7 @@ def children_rql(self): """XXX returns RQL to get children""" - def __iter__(self): + def iterchildren(self): """iterates over the item's children""" def is_leaf(self): diff -r 12ea53a4c0da -r 387d51af966d misc/migration/3.5.3_Any.py --- a/misc/migration/3.5.3_Any.py Tue Oct 13 18:21:24 2009 +0200 +++ b/misc/migration/3.5.3_Any.py Thu Oct 15 10:31:54 2009 +0200 @@ -1,7 +1,7 @@ # type attribute might already be there if migrating from # version < 3.5 to version >= 3.5.3, BaseTransition being added # in bootstrap_migration -if versions_map['cubicweb'][0] < (3, 5, 0): +if versions_map['cubicweb'][0] >= (3, 5, 0): add_attribute('BaseTransition', 'type') sync_schema_props_perms('state_of') sync_schema_props_perms('transition_of') diff -r 12ea53a4c0da -r 387d51af966d req.py --- a/req.py Tue Oct 13 18:21:24 2009 +0200 +++ b/req.py Thu Oct 15 10:31:54 2009 +0200 @@ -108,31 +108,55 @@ # XXX move to CWEntityManager or even better as factory method (unclear # where yet...) - def create_entity(self, etype, *args, **kwargs): + def create_entity(self, etype, **kwargs): """add a new entity of the given type Example (in a shell session): - c = create_entity('Company', name='Logilab') - create_entity('Person', ('works_for', 'Y'), Y=c.eid, firstname='John', lastname='Doe') + c = create_entity('Company', name=u'Logilab') + create_entity('Person', works_for=c, firstname=u'John', lastname=u'Doe') + """ rql = 'INSERT %s X' % etype relations = [] - restrictions = [] + restrictions = set() cachekey = [] - for rtype, rvar in args: - relations.append('X %s %s' % (rtype, rvar)) - restrictions.append('%s eid %%(%s)s' % (rvar, rvar)) - cachekey.append(rvar) - for attr in kwargs: - if attr in cachekey: - continue - relations.append('X %s %%(%s)s' % (attr, attr)) + pending_relations = [] + for attr, value in kwargs.iteritems(): + if isinstance(value, (tuple, list, set, frozenset)): + if len(value) == 1: + value = iter(value).next() + else: + pending_relations.append( (attr, value) ) + continue + if hasattr(value, 'eid'): # non final relation + rvar = attr.upper() + # XXX safer detection of object relation + if attr.startswith('reverse_'): + relations.append('%s %s X' % (rvar, attr[len('reverse_'):])) + else: + relations.append('X %s %s' % (attr, rvar)) + restriction = '%s eid %%(%s)s' % (rvar, attr) + if not restriction in restrictions: + restrictions.add(restriction) + cachekey.append(attr) + kwargs[attr] = value.eid + else: # attribute + relations.append('X %s %%(%s)s' % (attr, attr)) if relations: rql = '%s: %s' % (rql, ', '.join(relations)) if restrictions: rql = '%s WHERE %s' % (rql, ', '.join(restrictions)) - return self.execute(rql, kwargs, cachekey).get_entity(0, 0) + created = self.execute(rql, kwargs, cachekey).get_entity(0, 0) + for attr, values in pending_relations: + if attr.startswith('reverse_'): + restr = 'Y %s X' % attr[len('reverse_'):] + else: + restr = 'X %s Y' % attr + self.execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % ( + restr, ','.join(str(r.eid) for r in values)), + {'x': created.eid}, 'x') + return created def ensure_ro_rql(self, rql): """raise an exception if the given rql is not a select query""" diff -r 12ea53a4c0da -r 387d51af966d selectors.py --- a/selectors.py Tue Oct 13 18:21:24 2009 +0200 +++ b/selectors.py Thu Oct 15 10:31:54 2009 +0200 @@ -43,7 +43,7 @@ __docformat__ = "restructuredtext en" import logging -from warnings import warn +from warnings import warn, filterwarnings from logilab.common.compat import all from logilab.common.interface import implements as implements_iface @@ -64,7 +64,7 @@ def lltrace(selector): # don't wrap selectors if not in development mode - if CubicWebConfiguration.mode == 'installed': + if CubicWebConfiguration.mode == 'system': # XXX config.debug return selector def traced(cls, *args, **kwargs): # /!\ lltrace decorates pure function or __call__ method, this diff -r 12ea53a4c0da -r 387d51af966d server/serverconfig.py --- a/server/serverconfig.py Tue Oct 13 18:21:24 2009 +0200 +++ b/server/serverconfig.py Thu Oct 15 10:31:54 2009 +0200 @@ -15,7 +15,7 @@ from logilab.common.decorators import wproperty, cached, clear_cache from cubicweb import CW_SOFTWARE_ROOT, RegistryNotFound -from cubicweb.toolsutils import env_path, read_config, restrict_perms_to_user +from cubicweb.toolsutils import read_config, restrict_perms_to_user from cubicweb.cwconfig import CubicWebConfiguration, merge_options from cubicweb.server import SOURCE_TYPES @@ -75,12 +75,6 @@ class ServerConfiguration(CubicWebConfiguration): """standalone RQL server""" name = 'repository' - if os.environ.get('APYCOT_ROOT'): - root = os.environ['APYCOT_ROOT'] - elif CubicWebConfiguration.mode == 'dev': - BACKUP_DIR = CubicWebConfiguration.RUNTIME_DIR - else: - BACKUP_DIR = '/var/lib/cubicweb/backup/' cubicweb_appobject_path = CubicWebConfiguration.cubicweb_appobject_path | set(['sobjects', 'hooks']) cube_appobject_path = CubicWebConfiguration.cube_appobject_path | set(['sobjects', 'hooks']) diff -r 12ea53a4c0da -r 387d51af966d server/sources/__init__.py --- a/server/sources/__init__.py Tue Oct 13 18:21:24 2009 +0200 +++ b/server/sources/__init__.py Thu Oct 15 10:31:54 2009 +0200 @@ -191,6 +191,9 @@ * if this source doesn't support the relation, can be crossed unless explicitly specified in .dont_cross_relations """ + # XXX find a way to have relation such as state_of in dont cross + # relation (eg composite relation without both end type available? + # card 1 relation ? ...) if self.support_relation(rtype): return rtype in self.cross_relations return rtype not in self.dont_cross_relations diff -r 12ea53a4c0da -r 387d51af966d server/test/unittest_msplanner.py --- a/server/test/unittest_msplanner.py Tue Oct 13 18:21:24 2009 +0200 +++ b/server/test/unittest_msplanner.py Thu Oct 15 10:31:54 2009 +0200 @@ -37,7 +37,7 @@ support_entities = {'Card': True, 'Note': True, 'State': True} support_relations = {'in_state': True, 'multisource_rel': True, 'multisource_inlined_rel': True, 'multisource_crossed_rel': True} - dont_cross_relations = set(('fiche', 'in_state')) + dont_cross_relations = set(('fiche', 'state_of')) cross_relations = set(('multisource_crossed_rel',)) def syntax_tree_search(self, *args, **kwargs): @@ -1977,6 +1977,14 @@ None, None, [self.system], {}, [])], {'x': 999999, 'u': 999998}) + def test_state_of_cross(self): + self._test('DELETE State X WHERE NOT X state_of Y', + [('DeleteEntitiesStep', + [('OneFetchStep', + [('Any X WHERE NOT X state_of Y, X is State, Y is Workflow', + [{'X': 'State', 'Y': 'Workflow'}])], + None, None, [self.system], {}, [])])] + ) class MSPlannerTwoSameExternalSourcesTC(BasePlannerTC): """test planner related feature on a 3-sources repository: diff -r 12ea53a4c0da -r 387d51af966d server/test/unittest_rql2sql.py --- a/server/test/unittest_rql2sql.py Tue Oct 13 18:21:24 2009 +0200 +++ b/server/test/unittest_rql2sql.py Thu Oct 15 10:31:54 2009 +0200 @@ -567,6 +567,15 @@ FROM cw_EmailAddress AS O WHERE NOT EXISTS(SELECT 1 FROM use_email_relation AS rel_use_email0 WHERE rel_use_email0.eid_from=1 AND rel_use_email0.eid_to=O.cw_eid) AND EXISTS(SELECT 1 FROM use_email_relation AS rel_use_email1 WHERE rel_use_email1.eid_to=O.cw_eid AND EXISTS(SELECT 1 FROM cw_CWGroup AS D WHERE rel_use_email1.eid_from=2 AND NOT EXISTS(SELECT 1 FROM in_group_relation AS rel_in_group2 WHERE rel_in_group2.eid_from=2 AND rel_in_group2.eid_to=D.cw_eid) AND D.cw_name=guests)) ORDER BY 4 DESC'''), + + + ("Any X WHERE X eid 0, X eid 0", + '''SELECT 0'''), + + ("Any X WHERE X eid 0, X eid 0, X test TRUE", + '''SELECT X.cw_eid +FROM cw_Personne AS X +WHERE X.cw_eid=0 AND X.cw_eid=0 AND X.cw_test='''), ] MULTIPLE_SEL = [ diff -r 12ea53a4c0da -r 387d51af966d test/unittest_entity.py --- a/test/unittest_entity.py Tue Oct 13 18:21:24 2009 +0200 +++ b/test/unittest_entity.py Thu Oct 15 10:31:54 2009 +0200 @@ -185,7 +185,19 @@ Personne.fetch_attrs, Personne.fetch_order = fetch_config(('nom', )) # XXX self.assertEquals(p.related_rql('evaluee'), - 'Any X,AA ORDERBY Z DESC WHERE X modification_date Z, E eid %(x)s, E evaluee X, X modification_date AA') + 'Any X,AA ORDERBY Z DESC ' + 'WHERE X modification_date Z, E eid %(x)s, E evaluee X, ' + 'X modification_date AA') + + tag = self.vreg['etypes'].etype_class('Tag')(self.request()) + self.assertEquals(tag.related_rql('tags', 'subject'), + 'Any X,AA ORDERBY Z DESC ' + 'WHERE X modification_date Z, E eid %(x)s, E tags X, ' + 'X modification_date AA') + self.assertEquals(tag.related_rql('tags', 'subject', ('Personne',)), + 'Any X,AA,AB ORDERBY AA ASC ' + 'WHERE E eid %(x)s, E tags XE is IN (Personne), X nom AA, ' + 'X modification_date AB') def test_unrelated_rql_security_1(self): user = self.request().user @@ -460,6 +472,21 @@ self.assertEquals(card.absolute_url(), 'http://testing.fr/cubicweb/card/eid/%s' % card.eid) + def test_create_entity(self): + p1 = self.add_entity('Personne', nom=u'fayolle', prenom=u'alexandre') + p2 = self.add_entity('Personne', nom=u'campeas', prenom=u'aurelien') + note = self.add_entity('Note', type=u'z') + req = self.request() + p = req.create_entity('Personne', nom=u'di mascio', prenom=u'adrien', + connait=p1, evaluee=[p1, p2], + reverse_ecrit_par=note) + self.assertEquals(p.nom, 'di mascio') + self.assertEquals([c.nom for c in p.connait], ['fayolle']) + self.assertEquals(sorted([c.nom for c in p.evaluee]), ['campeas', 'fayolle']) + self.assertEquals([c.type for c in p.reverse_ecrit_par], ['z']) + + + if __name__ == '__main__': from logilab.common.testlib import unittest_main unittest_main() diff -r 12ea53a4c0da -r 387d51af966d vregistry.py --- a/vregistry.py Tue Oct 13 18:21:24 2009 +0200 +++ b/vregistry.py Thu Oct 15 10:31:54 2009 +0200 @@ -202,7 +202,7 @@ % (args, kwargs.keys(), [repr(v) for v in appobjects])) if len(winners) > 1: - if self.config.mode == 'installed': + if self.config.debugmode: self.error('select ambiguity, args: %s\nkwargs: %s %s', args, kwargs.keys(), [repr(v) for v in winners]) else: diff -r 12ea53a4c0da -r 387d51af966d web/views/editcontroller.py --- a/web/views/editcontroller.py Tue Oct 13 18:21:24 2009 +0200 +++ b/web/views/editcontroller.py Thu Oct 15 10:31:54 2009 +0200 @@ -157,7 +157,7 @@ todelete = self._cw.list_form_param('__delete', formparams, pop=True) self.delete_relations(parse_relations_descr(todelete)) if formparams.has_key('__cloned_eid'): - entity.copy_relations(formparams['__cloned_eid']) + entity.copy_relations(typed_eid(formparams['__cloned_eid'])) if formparams.has_key('__insert'): toinsert = self._cw.list_form_param('__insert', formparams, pop=True) self.insert_relations(parse_relations_descr(toinsert)) diff -r 12ea53a4c0da -r 387d51af966d web/views/editforms.py --- a/web/views/editforms.py Tue Oct 13 18:21:24 2009 +0200 +++ b/web/views/editforms.py Thu Oct 15 10:31:54 2009 +0200 @@ -177,6 +177,7 @@ form = self._build_form( entity, rtype, role, default, onsubmit, reload) if not self.should_edit_attribute(entity, rschema, role, form): + self.w(entity.printable_value(rtype)) return value = entity.printable_value(rtype) or default self.attribute_form(lzone, value, form, @@ -184,13 +185,15 @@ else: if rvid is None: rvid = self._compute_best_vid(entity.e_schema, rschema, role) - if not self.should_edit_relation(entity, rschema, role, rvid): - return rset = entity.related(rtype, role) if rset: value = self._cw.view(rvid, rset) else: value = default + if not self.should_edit_relation(entity, rschema, role, rvid): + if rset: + self.w(value) + return onsubmit = ("return inlineValidateRelationForm('%(rtype)s', '%(role)s', '%(eid)s', " "'%(divid)s', %(reload)s, '%(vid)s', '%(default)s', '%(lzone)s');") form = self._build_form( diff -r 12ea53a4c0da -r 387d51af966d web/webconfig.py --- a/web/webconfig.py Tue Oct 13 18:21:24 2009 +0200 +++ b/web/webconfig.py Thu Oct 15 10:31:54 2009 +0200 @@ -172,7 +172,7 @@ ('print-traceback', {'type' : 'yn', - 'default': not CubicWebConfiguration.mode == 'installed', + 'default': CubicWebConfiguration.mode != 'system', 'help': 'print the traceback on the error page when an error occured', 'group': 'web', 'inputlevel': 2, }),