# HG changeset patch # User Adrien Di Mascio # Date 1248355983 -7200 # Node ID 4d114865098f1ded6e3f1ea37cc185dcc74357b4 # Parent d0f31e11993602ce828b378674a220866bcbd90a# Parent 986718a355fafea8d0480225eb870136d2932c80 backport stable branch into default diff -r 986718a355fa -r 4d114865098f __init__.py --- a/__init__.py Thu Jul 23 12:55:51 2009 +0200 +++ b/__init__.py Thu Jul 23 15:33:03 2009 +0200 @@ -109,11 +109,16 @@ # url generation methods ################################################## - def build_url(self, method, base_url=None, **kwargs): + def build_url(self, *args, **kwargs): """return an absolute URL using params dictionary key/values as URL parameters. Values are automatically URL quoted, and the publishing method to use may be specified or will be guessed. """ + # use *args since we don't want first argument to be "anonymous" to + # avoid potential clash with kwargs + assert len(args) == 1, 'only 0 or 1 non-named-argument expected' + method = args[0] + base_url = kwargs.pop('base_url', None) if base_url is None: base_url = self.base_url() if '_restpath' in kwargs: diff -r 986718a355fa -r 4d114865098f appobject.py --- a/appobject.py Thu Jul 23 12:55:51 2009 +0200 +++ b/appobject.py Thu Jul 23 15:33:03 2009 +0200 @@ -151,7 +151,8 @@ # try to get page boundaries from the navigation component # XXX we should probably not have a ref to this component here (eg in # cubicweb.common) - nav = self.vreg.select_component('navigation', self.req, self.rset) + nav = self.vreg.select_object('components', 'navigation', self.req, + rset=self.rset) if nav: start, stop = nav.page_boundaries() rql = self._limit_offset_rql(stop - start, start) @@ -189,7 +190,8 @@ def view(self, __vid, rset=None, __fallback_vid=None, **kwargs): """shortcut to self.vreg.view method avoiding to pass self.req""" - return self.vreg.view(__vid, self.req, rset, __fallback_vid, **kwargs) + return self.vreg.render(__vid, self.req, __fallback_vid, rset=rset, + **kwargs) def initialize_varmaker(self): varmaker = self.req.get_page_data('rql_varmaker') @@ -202,11 +204,18 @@ controller = 'view' - def build_url(self, method=None, **kwargs): + def build_url(self, *args, **kwargs): """return an absolute URL using params dictionary key/values as URL parameters. Values are automatically URL quoted, and the publishing method to use may be specified or will be guessed. """ + # use *args since we don't want first argument to be "anonymous" to + # avoid potential clash with kwargs + if args: + assert len(args) == 1, 'only 0 or 1 non-named-argument expected' + method = args[0] + else: + method = None # XXX I (adim) think that if method is passed explicitly, we should # not try to process it and directly call req.build_url() if method is None: diff -r 986718a355fa -r 4d114865098f common/migration.py --- a/common/migration.py Thu Jul 23 12:55:51 2009 +0200 +++ b/common/migration.py Thu Jul 23 15:33:03 2009 +0200 @@ -11,7 +11,7 @@ import sys import os import logging -from tempfile import mktemp +import tempfile from os.path import exists, join, basename, splitext from logilab.common.decorators import cached @@ -337,7 +337,7 @@ configfile = self.config.main_config_file() if self._option_changes: read_old_config(self.config, self._option_changes, configfile) - newconfig = mktemp() + newconfig = mkstemp() for optdescr in self._option_changes: if optdescr[0] == 'added': optdict = self.config.get_option_def(optdescr[1]) diff -r 986718a355fa -r 4d114865098f common/mixins.py --- a/common/mixins.py Thu Jul 23 12:55:51 2009 +0200 +++ b/common/mixins.py Thu Jul 23 15:33:03 2009 +0200 @@ -244,7 +244,7 @@ @obsolete('use EntityFieldsForm.subject_in_state_vocabulary') def subject_in_state_vocabulary(self, rschema, limit=None): - form = self.vreg.select_object('forms', 'edition', self.req, entity=self) + form = self.vreg.select('forms', 'edition', self.req, entity=self) return form.subject_in_state_vocabulary(rschema, limit) diff -r 986718a355fa -r 4d114865098f common/uilib.py --- a/common/uilib.py Thu Jul 23 12:55:51 2009 +0200 +++ b/common/uilib.py Thu Jul 23 15:33:03 2009 +0200 @@ -215,7 +215,7 @@ def simple_sgml_tag(tag, content=None, escapecontent=True, **attrs): """generation of a simple sgml tag (eg without children tags) easier - content and attributes will be escaped + content and attri butes will be escaped """ value = u'<%s' % tag if attrs: diff -r 986718a355fa -r 4d114865098f cwconfig.py --- a/cwconfig.py Thu Jul 23 12:55:51 2009 +0200 +++ b/cwconfig.py Thu Jul 23 15:33:03 2009 +0200 @@ -149,7 +149,7 @@ 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')): + elif exists(join(CW_SOFTWARE_ROOT, '.hg')) or os.environ.get('CW_MODE') == 'user': mode = 'dev' CUBES_DIR = abspath(normpath(join(CW_SOFTWARE_ROOT, '../cubes'))) else: @@ -581,24 +581,24 @@ @classmethod def runtime_dir(cls): """run time directory for pid file...""" - return env_path('CW_RUNTIME', cls.RUNTIME_DIR, 'run time') + return env_path('CW_RUNTIME_DIR', cls.RUNTIME_DIR, 'run time') @classmethod def registry_dir(cls): """return the control directory""" - return env_path('CW_REGISTRY', cls.REGISTRY_DIR, 'registry') + return env_path('CW_INSTANCES_DIR', cls.REGISTRY_DIR, 'registry') @classmethod def instance_data_dir(cls): """return the instance data directory""" - return env_path('CW_INSTANCE_DATA', + return env_path('CW_INSTANCES_DATA_DIR', cls.INSTANCE_DATA_DIR or cls.REGISTRY_DIR, 'additional data') @classmethod def migration_scripts_dir(cls): """cubicweb migration scripts directory""" - return env_path('CW_MIGRATION', cls.MIGRATION_DIR, 'migration') + return env_path('CW_MIGRATION_DIR', cls.MIGRATION_DIR, 'migration') @classmethod def config_for(cls, appid, config=None): diff -r 986718a355fa -r 4d114865098f cwvreg.py --- a/cwvreg.py Thu Jul 23 12:55:51 2009 +0200 +++ b/cwvreg.py Thu Jul 23 15:33:03 2009 +0200 @@ -9,6 +9,7 @@ _ = unicode from logilab.common.decorators import cached, clear_cache +from logilab.common.deprecation import obsolete from rql import RQLHelper @@ -39,7 +40,26 @@ class CubicWebRegistry(VRegistry): - """extend the generic VRegistry with some cubicweb specific stuff""" + """Central registry for the cubicweb application, extending the generic + VRegistry with some cubicweb specific stuff. + + This is one of the central object in cubicweb application, coupling + dynamically loaded objects with the schema and the configuration objects. + + It specializes the VRegistry by adding some convenience methods to access to + stored objects. Currently we have the following registries of objects known + by the web application (library may use some others additional registries): + + * etypes + * views + * components + * actions + * forms + * formrenderers + * controllers, which are directly plugged into the application + object to handle request publishing XXX to merge with views + * contentnavigation XXX to merge with components? to kill? + """ def __init__(self, config, debug=None, initlog=True): if initlog: @@ -175,7 +195,7 @@ """ etype = str(etype) if etype == 'Any': - return self.select(self.registry_objects('etypes', 'Any'), 'Any') + return self.select('etypes', 'Any', 'Any') eschema = self.schema.eschema(etype) baseschemas = [eschema] + eschema.ancestors() # browse ancestors from most specific to most generic and @@ -186,42 +206,47 @@ except KeyError: btype = str(baseschema) try: - cls = self.select(self.registry_objects('etypes', btype), etype) + cls = self.select('etypes', btype, etype) break except ObjectNotFound: pass else: # no entity class for any of the ancestors, fallback to the default # one - cls = self.select(self.registry_objects('etypes', 'Any'), etype) + cls = self.select('etypes', 'Any', etype) return cls - def render(self, registry, oid, req, **context): - """select an object in a given registry and render it - - - registry: the registry's name - - oid : the view to call - - req : the HTTP request + def render(self, __oid, req, __fallback_oid=None, __registry='views', + rset=None, **kwargs): + """select object, or fallback object if specified and the first one + isn't selectable, then render it """ - objclss = self.registry_objects(registry, oid) try: - rset = context.pop('rset') - except KeyError: - rset = None - selected = self.select(objclss, req, rset, **context) - return selected.render(**context) + obj = self.select(__registry, __oid, req, rset=rset, **kwargs) + except NoSelectableObject: + if __fallback_oid is None: + raise + obj = self.select(__registry, __fallback_oid, req, rset=rset, + **kwargs) + return obj.render(**kwargs) def main_template(self, req, oid='main-template', **context): """display query by calling the given template (default to main), and returning the output as a string instead of requiring the [w]rite method as argument """ - res = self.render('views', oid, req, **context) + res = self.render(oid, req, **context) if isinstance(res, unicode): return res.encode(req.encoding) assert isinstance(res, str) return res + def select_vobject(self, registry, oid, *args, **kwargs): + selected = self.select_object(registry, oid, *args, **kwargs) + if selected and selected.propval('visible'): + return selected + return None + def possible_vobjects(self, registry, *args, **kwargs): """return an ordered list of possible app objects in a given registry, supposing they support the 'visible' and 'order' properties (as most @@ -231,9 +256,9 @@ key=lambda x: x.propval('order')) if x.propval('visible')] - def possible_actions(self, req, rset, **kwargs): + def possible_actions(self, req, rset=None, **kwargs): if rset is None: - actions = self.possible_vobjects('actions', req, rset, **kwargs) + actions = self.possible_vobjects('actions', req, rset=rset, **kwargs) else: actions = rset.possible_actions(**kwargs) # cached implementation result = {} @@ -241,7 +266,7 @@ result.setdefault(action.category, []).append(action) return result - def possible_views(self, req, rset, **kwargs): + def possible_views(self, req, rset=None, **kwargs): """return an iterator on possible views for this result set views returned are classes, not instances @@ -250,50 +275,34 @@ if vid[0] == '_': continue try: - view = self.select(views, req, rset, **kwargs) + view = self.select_best(views, req, rset=rset, **kwargs) if view.linkable(): yield view except NoSelectableObject: continue except Exception: - self.exception('error while trying to list possible %s views for %s', + self.exception('error while trying to select %s view for %s', vid, rset) - def view(self, __vid, req, rset=None, __fallback_vid=None, **kwargs): - """shortcut to self.vreg.render method avoiding to pass self.req""" - try: - view = self.select_view(__vid, req, rset, **kwargs) - except NoSelectableObject: - if __fallback_vid is None: - raise - view = self.select_view(__fallback_vid, req, rset, **kwargs) - return view.render(**kwargs) - + @obsolete("use .select_object('boxes', ...)") def select_box(self, oid, *args, **kwargs): """return the most specific view according to the result set""" - try: - return self.select_object('boxes', oid, *args, **kwargs) - except NoSelectableObject: - return + return self.select_object('boxes', oid, *args, **kwargs) + @obsolete("use .select_object('components', ...)") + def select_component(self, cid, *args, **kwargs): + """return the most specific component according to the result set""" + return self.select_object('components', cid, *args, **kwargs) + + @obsolete("use .select_object('actions', ...)") def select_action(self, oid, *args, **kwargs): """return the most specific view according to the result set""" - try: - return self.select_object('actions', oid, *args, **kwargs) - except NoSelectableObject: - return + return self.select_object('actions', oid, *args, **kwargs) - def select_component(self, cid, *args, **kwargs): - """return the most specific component according to the result set""" - try: - return self.select_object('components', cid, *args, **kwargs) - except (NoSelectableObject, ObjectNotFound): - return - + @obsolete("use .select('views', ...)") def select_view(self, __vid, req, rset=None, **kwargs): """return the most specific view according to the result set""" - views = self.registry_objects('views', __vid) - return self.select(views, req, rset, **kwargs) + return self.select('views', __vid, req, rset=rset, **kwargs) # properties handling ##################################################### @@ -388,7 +397,7 @@ """special registry to be used when an application has to deal with connections to differents repository. This class add some additional wrapper trying to hide buggy class attributes since classes are not designed to be - shared. + shared among multiple registries. """ def etype_class(self, etype): """return an entity class for the given entity type. @@ -401,25 +410,27 @@ usercls.e_schema = self.schema.eschema(etype) return usercls - def select(self, vobjects, *args, **kwargs): + def select_best(self, vobjects, *args, **kwargs): """return an instance of the most specific object according to parameters - raise NoSelectableObject if not object apply + raise NoSelectableObject if no object apply """ - for vobject in vobjects: - vobject.vreg = self - vobject.schema = self.schema - vobject.config = self.config - selected = super(MulCnxCubicWebRegistry, self).select(vobjects, *args, - **kwargs) + for vobjectcls in vobjects: + self._fix_cls_attrs(vobjectcls) + selected = super(MulCnxCubicWebRegistry, self).select_best( + 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 + self._fix_cls_attrs(selected) return selected + def _fix_cls_attrs(self, vobject): + vobject.vreg = self + vobject.schema = self.schema + vobject.config = self.config + + from datetime import datetime, date, time, timedelta YAMS_TO_PY = { diff -r 986718a355fa -r 4d114865098f debian/control --- a/debian/control Thu Jul 23 12:55:51 2009 +0200 +++ b/debian/control Thu Jul 23 15:33:03 2009 +0200 @@ -76,8 +76,8 @@ 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.43.0), python-yams (>= 0.23.0), python-rql (>= 0.22.1) -Recommends: python-simpletal (>= 4.0), python-lxml +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), python-lxml +Recommends: python-simpletal (>= 4.0) Conflicts: cubicweb-core Replaces: cubicweb-core Description: common library for the CubicWeb framework diff -r 986718a355fa -r 4d114865098f devtools/_apptest.py --- a/devtools/_apptest.py Thu Jul 23 12:55:51 2009 +0200 +++ b/devtools/_apptest.py Thu Jul 23 15:33:03 2009 +0200 @@ -177,7 +177,7 @@ self.create_request(rql=rql, **optional_args or {})) def check_view(self, rql, vid, optional_args, template='main'): - """checks if vreg.view() raises an exception in this environment + """checks if rendering view raises an exception in this environment If any exception is raised in this method, it will be considered as a TestFailure @@ -186,7 +186,6 @@ template=template, optional_args=optional_args) def call_view(self, vid, rql, template='main', optional_args=None): - """shortcut for self.vreg.view()""" assert template if optional_args is None: optional_args = {} @@ -196,7 +195,7 @@ def call_edit(self, req): """shortcut for self.app.edit()""" - controller = self.app.select_controller('edit', req) + controller = self.vreg.select('controllers', 'edit', req) try: controller.publish() except Redirect: @@ -224,7 +223,7 @@ def iter_possible_actions(self, req, rset): """returns a list of possible vids for """ - for action in self.vreg.possible_vobjects('actions', req, rset): + for action in self.vreg.possible_vobjects('actions', req, rset=rset): yield action class ExistingTestEnvironment(TestEnvironment): diff -r 986718a355fa -r 4d114865098f devtools/apptest.py --- a/devtools/apptest.py Thu Jul 23 12:55:51 2009 +0200 +++ b/devtools/apptest.py Thu Jul 23 15:33:03 2009 +0200 @@ -34,6 +34,14 @@ def message(self): return message_from_string(self.msg) + @property + def subject(self): + return self.message.get('Subject') + + @property + def content(self): + return self.message.get_payload(decode=True) + def __repr__(self): return '' % (','.join(self.recipients), self.message.get('Subject')) @@ -219,18 +227,18 @@ return sorted((a.id, a.__class__) for a in self.vreg.possible_views(req, rset)) def pactions(self, req, rset, skipcategories=('addrelated', 'siteactions', 'useractions')): - return [(a.id, a.__class__) for a in self.vreg.possible_vobjects('actions', req, rset) + return [(a.id, a.__class__) for a in self.vreg.possible_vobjects('actions', req, rset=rset) if a.category not in skipcategories] def pactions_by_cats(self, req, rset, categories=('addrelated',)): - return [(a.id, a.__class__) for a in self.vreg.possible_vobjects('actions', req, rset) + return [(a.id, a.__class__) for a in self.vreg.possible_vobjects('actions', req, rset=rset) if a.category in categories] paddrelactions = deprecated_function(pactions_by_cats) def pactionsdict(self, req, rset, skipcategories=('addrelated', 'siteactions', 'useractions')): res = {} - for a in self.vreg.possible_vobjects('actions', req, rset): + for a in self.vreg.possible_vobjects('actions', req, rset=rset): if a.category not in skipcategories: res.setdefault(a.category, []).append(a.__class__) return res @@ -241,7 +249,7 @@ dump = simplejson.dumps args = [dump(arg) for arg in args] req = self.request(fname=fname, pageid='123', arg=args) - ctrl = self.env.app.select_controller('json', req) + ctrl = self.vreg.select('controllers', 'json', req) return ctrl.publish(), req # default test setup and teardown ######################################### @@ -286,7 +294,7 @@ def setUp(self): super(ControllerTC, self).setUp() self.req = self.request() - self.ctrl = self.env.app.select_controller('edit', self.req) + self.ctrl = self.vreg.select('controllers', 'edit', self.req) def publish(self, req): assert req is self.ctrl.req @@ -300,7 +308,7 @@ def expect_redirect_publish(self, req=None): if req is not None: - self.ctrl = self.env.app.select_controller('edit', req) + self.ctrl = self.vreg.select('controllers', 'edit', req) else: req = self.req try: diff -r 986718a355fa -r 4d114865098f devtools/devctl.py --- a/devtools/devctl.py Thu Jul 23 12:55:51 2009 +0200 +++ b/devtools/devctl.py Thu Jul 23 15:33:03 2009 +0200 @@ -254,13 +254,12 @@ if args: raise BadCommandUsage('Too much arguments') import shutil - from tempfile import mktemp + import tempfile import yams from logilab.common.fileutils import ensure_fs_mode from logilab.common.shellutils import globfind, find, rm from cubicweb.common.i18n import extract_from_tal, execute - tempdir = mktemp() - mkdir(tempdir) + tempdir = tempdir.mkdtemp() potfiles = [join(I18NDIR, 'entities.pot')] print '-> extract schema messages.' schemapot = join(tempdir, 'schema.pot') @@ -347,14 +346,13 @@ def update_cube_catalogs(cubedir): import shutil - from tempfile import mktemp + import tempfile from logilab.common.fileutils import ensure_fs_mode from logilab.common.shellutils import find, rm from cubicweb.common.i18n import extract_from_tal, execute toedit = [] cube = basename(normpath(cubedir)) - tempdir = mktemp() - mkdir(tempdir) + tempdir = tempfile.mkdtemp() print underline_title('Updating i18n catalogs for cube %s' % cube) chdir(cubedir) potfiles = [join('i18n', scfile) for scfile in ('entities.pot',) diff -r 986718a355fa -r 4d114865098f devtools/fake.py --- a/devtools/fake.py Thu Jul 23 12:55:51 2009 +0200 +++ b/devtools/fake.py Thu Jul 23 15:33:03 2009 +0200 @@ -154,6 +154,25 @@ return self.execute(*args, **kwargs) +# class FakeRequestNoCnx(FakeRequest): +# def get_session_data(self, key, default=None, pop=False): +# """return value associated to `key` in session data""" +# if pop: +# return self._session_data.pop(key, default) +# else: +# return self._session_data.get(key, default) + +# def set_session_data(self, key, value): +# """set value associated to `key` in session data""" +# self._session_data[key] = value + +# def del_session_data(self, key): +# try: +# del self._session_data[key] +# except KeyError: +# pass + + class FakeUser(object): login = 'toto' eid = 0 diff -r 986718a355fa -r 4d114865098f devtools/testlib.py --- a/devtools/testlib.py Thu Jul 23 12:55:51 2009 +0200 +++ b/devtools/testlib.py Thu Jul 23 15:33:03 2009 +0200 @@ -44,7 +44,7 @@ # compute how many entities by type we need to be able to satisfy relation constraint relmap = {} for rschema in schema.relations(): - if rschema.meta or rschema.is_final(): # skip meta relations + if rschema.is_final(): continue for subj, obj in rschema.iter_rdefs(): card = rschema.rproperty(subj, obj, 'cardinality') @@ -172,7 +172,8 @@ return validator.parse_string(output.strip()) - def view(self, vid, rset, req=None, template='main-template', **kwargs): + def view(self, vid, rset=None, req=None, template='main-template', + **kwargs): """This method tests the view `vid` on `rset` using `template` If no error occured while rendering the view, the HTML is analyzed @@ -183,7 +184,8 @@ """ req = req or rset and rset.req or self.request() req.form['vid'] = vid - view = self.vreg.select_view(vid, req, rset, **kwargs) + kwargs['rset'] = rset + view = self.vreg.select('views', vid, req, **kwargs) # set explicit test description if rset is not None: self.set_description("testing %s, mod=%s (%s)" % ( @@ -194,9 +196,11 @@ if template is None: # raw view testing, no template viewfunc = view.render else: - templateview = self.vreg.select_view(template, req, rset, view=view, **kwargs) kwargs['view'] = view - viewfunc = lambda **k: self.vreg.main_template(req, template, **kwargs) + templateview = self.vreg.select('views', template, req, **kwargs) + viewfunc = lambda **k: self.vreg.main_template(req, template, + **kwargs) + kwargs.pop('rset') return self._test_view(viewfunc, view, template, kwargs) @@ -278,7 +282,7 @@ and not issubclass(view, NotificationView)] if views: try: - view = self.vreg.select(views, req, rset) + view = self.vreg.select_best(views, req, rset=rset) if view.linkable(): yield view else: @@ -291,13 +295,13 @@ def list_actions_for(self, rset): """returns the list of actions that can be applied on `rset`""" req = rset.req - for action in self.vreg.possible_objects('actions', req, rset): + for action in self.vreg.possible_objects('actions', req, rset=rset): yield action def list_boxes_for(self, rset): """returns the list of boxes that can be applied on `rset`""" req = rset.req - for box in self.vreg.possible_objects('boxes', req, rset): + for box in self.vreg.possible_objects('boxes', req, rset=rset): yield box def list_startup_views(self): @@ -383,9 +387,9 @@ requestcls=testclass.requestcls) vreg = env.vreg vreg._selected = {} - orig_select = vreg.__class__.select - def instr_select(self, *args, **kwargs): - selected = orig_select(self, *args, **kwargs) + orig_select_best = vreg.__class__.select_best + def instr_select_best(self, *args, **kwargs): + selected = orig_select_best(self, *args, **kwargs) try: self._selected[selected.__class__] += 1 except KeyError: @@ -393,7 +397,7 @@ except AttributeError: pass # occurs on vreg used to restore database return selected - vreg.__class__.select = instr_select + vreg.__class__.select_best = instr_select_best def print_untested_objects(testclass, skipregs=('hooks', 'etypes')): vreg = testclass._env.vreg diff -r 986718a355fa -r 4d114865098f doc/book/en/admin/instance-config.rst --- a/doc/book/en/admin/instance-config.rst Thu Jul 23 12:55:51 2009 +0200 +++ b/doc/book/en/admin/instance-config.rst Thu Jul 23 15:33:03 2009 +0200 @@ -6,7 +6,7 @@ While creating an instance, a configuration file is generated in:: - $ (CW_REGISTRY) / / .conf + $ (CW_INSTANCES_DIR) / / .conf For example:: diff -r 986718a355fa -r 4d114865098f doc/book/en/admin/setup.rst --- a/doc/book/en/admin/setup.rst Thu Jul 23 12:55:51 2009 +0200 +++ b/doc/book/en/admin/setup.rst Thu Jul 23 15:33:03 2009 +0200 @@ -69,6 +69,10 @@ hg fclone http://www.logilab.org/hg/forests/cubicweb See :ref:`MercurialPresentation` for more details about Mercurial. +When cloning a repository, you might be set in a development branch +(the 'default' branch). You should check that the branches of the +repositories are set to 'stable' (using `hg up stable` for each one) +if you do not intend to develop the framework itself. In both cases, make sure you have installed the dependencies (see appendixes for the list). @@ -105,19 +109,20 @@ Add the following lines to either `.bashrc` or `.bash_profile` to configure your development environment :: - export PYTHONPATH=/full/path/to/cubicweb-forest + export PYTHONPATH=/full/path/to/cubicweb-forest -If you installed the debian packages, no configuration is required. -Your new cubes will be placed in `/usr/share/cubicweb/cubes` and -your applications will be placed in `/etc/cubicweb.d`. +If you installed *CubicWeb* with packages, no configuration is required and your +new cubes will be placed in `/usr/share/cubicweb/cubes` and your applications +will be placed in `/etc/cubicweb.d`. -To use others directories then you will have to configure the -following environment variables as follows:: +You may run a system-wide install of *CubicWeb* in "user mode" and use it for +development by setting the following environment variable:: + export CW_MODE=user export CW_CUBES_PATH=~/lib/cubes - export CW_REGISTRY=~/etc/cubicweb.d/ - export CW_INSTANCE_DATA=$CW_REGISTRY - export CW_RUNTIME=/tmp + export CW_INSTANCES_DIR=~/etc/cubicweb.d/ + export CW_INSTANCES_DATA_DIR=$CW_INSTANCES_DIR + export CW_RUNTIME_DIR=/tmp .. note:: The values given above are our suggestions but of course diff -r 986718a355fa -r 4d114865098f doc/book/en/annexes/faq.rst --- a/doc/book/en/annexes/faq.rst Thu Jul 23 12:55:51 2009 +0200 +++ b/doc/book/en/annexes/faq.rst Thu Jul 23 15:33:03 2009 +0200 @@ -367,3 +367,12 @@ $ psql mydb mydb=> update cw_cwuser set cw_upassword='qHO8282QN5Utg' where cw_login='joe'; UPDATE 1 + +I've just created a user in a group and it doesn't work ! +--------------------------------------------------------- + +You are probably getting errors such as :: + + remove {'PR': 'Project', 'C': 'CWUser'} from solutions since your_user has no read access to cost + +This is because you have to put your user in the "users" group. The user has to be in both groups. diff -r 986718a355fa -r 4d114865098f doc/book/en/intro/concepts/index.rst --- a/doc/book/en/intro/concepts/index.rst Thu Jul 23 12:55:51 2009 +0200 +++ b/doc/book/en/intro/concepts/index.rst Thu Jul 23 15:33:03 2009 +0200 @@ -64,7 +64,7 @@ On a Unix system, the instances are usually stored in the directory :file:`/etc/cubicweb.d/`. During development, the :file:`~/etc/cubicweb.d/` -directory is looked up, as well as the paths in :envvar:`CW_REGISTRY` +directory is looked up, as well as the paths in :envvar:`CW_INSTANCES_DIR` environment variable. The term application can refer to an instance or to a cube, depending on the diff -r 986718a355fa -r 4d114865098f entities/__init__.py --- a/entities/__init__.py Thu Jul 23 12:55:51 2009 +0200 +++ b/entities/__init__.py Thu Jul 23 15:33:03 2009 +0200 @@ -242,12 +242,12 @@ @obsolete('use EntityFieldsForm.subject_relation_vocabulary') def subject_relation_vocabulary(self, rtype, limit): - form = self.vreg.select_object('forms', 'edition', self.req, entity=self) + form = self.vreg.select('forms', 'edition', self.req, entity=self) return form.subject_relation_vocabulary(rtype, limit) @obsolete('use EntityFieldsForm.object_relation_vocabulary') def object_relation_vocabulary(self, rtype, limit): - form = self.vreg.select_object('forms', 'edition', self.req, entity=self) + form = self.vreg.select('forms', 'edition', self.req, entity=self) return form.object_relation_vocabulary(rtype, limit) @obsolete('use AutomaticEntityForm.[e]relations_by_category') diff -r 986718a355fa -r 4d114865098f entities/schemaobjs.py --- a/entities/schemaobjs.py Thu Jul 23 12:55:51 2009 +0200 +++ b/entities/schemaobjs.py Thu Jul 23 15:33:03 2009 +0200 @@ -25,8 +25,6 @@ def dc_long_title(self): stereotypes = [] _ = self.req._ - if self.meta: - stereotypes.append(_('meta')) if self.final: stereotypes.append(_('final')) if stereotypes: @@ -48,8 +46,6 @@ def dc_long_title(self): stereotypes = [] _ = self.req._ - if self.meta: - stereotypes.append(_('meta')) if self.symetric: stereotypes.append(_('symetric')) if self.inlined: diff -r 986718a355fa -r 4d114865098f entity.py --- a/entity.py Thu Jul 23 12:55:51 2009 +0200 +++ b/entity.py Thu Jul 23 15:33:03 2009 +0200 @@ -23,13 +23,9 @@ from cubicweb.appobject import AppRsetObject from cubicweb.schema import RQLVocabularyConstraint, RQLConstraint, bw_normalize_etype -try: - from cubicweb.common.uilib import printable_value, soup2xhtml - from cubicweb.common.mixins import MI_REL_TRIGGERS - from cubicweb.common.mttransforms import ENGINE -except ImportError: - # missing -common - MI_REL_TRIGGERS = {} +from cubicweb.common.uilib import printable_value, soup2xhtml +from cubicweb.common.mixins import MI_REL_TRIGGERS +from cubicweb.common.mttransforms import ENGINE _marker = object() @@ -368,13 +364,20 @@ def has_perm(self, action): return self.e_schema.has_perm(self.req, action, self.eid) - def view(self, vid, __registry='views', **kwargs): + def view(self, vid, **kwargs): """shortcut to apply a view on this entity""" - return self.vreg.render(__registry, vid, self.req, rset=self.rset, + return self.vreg.render(vid, self.req, rset=self.rset, row=self.row, col=self.col, **kwargs) - def absolute_url(self, method=None, **kwargs): + def absolute_url(self, *args, **kwargs): """return an absolute url to view this entity""" + # use *args since we don't want first argument to be "anonymous" to + # avoid potential clash with kwargs + if args: + assert len(args) == 1, 'only 0 or 1 non-named-argument expected' + method = args[0] + else: + method = None # in linksearch mode, we don't want external urls else selecting # the object for use in the relation is tricky # XXX search_state is web specific @@ -478,7 +481,7 @@ assert self.has_eid() execute = self.req.execute for rschema in self.e_schema.subject_relations(): - if rschema.meta or rschema.is_final(): + if rschema.is_final() or rschema.meta: continue # skip already defined relations if getattr(self, rschema.type): @@ -726,7 +729,7 @@ interpreted as a separator in case vocabulary results are grouped """ from logilab.common.testlib import mock_object - form = self.vreg.select_object('forms', 'edition', self.req, entity=self) + form = self.vreg.select('forms', 'edition', self.req, entity=self) field = mock_object(name=rtype, role=role) return form.form_field_vocabulary(field, limit) diff -r 986718a355fa -r 4d114865098f etwist/server.py --- a/etwist/server.py Thu Jul 23 12:55:51 2009 +0200 +++ b/etwist/server.py Thu Jul 23 15:33:03 2009 +0200 @@ -106,10 +106,7 @@ self.pyro_listen_timeout = 0.02 start_task(1, self.pyro_loop_event) self.appli.repo.start_looping_tasks() - try: - self.url_rewriter = self.appli.vreg.select_component('urlrewriter') - except ObjectNotFound: - self.url_rewriter = None + self.url_rewriter = self.appli.vreg.select_object('components', 'urlrewriter') interval = min(config['cleanup-session-time'] or 120, config['cleanup-anonymous-session-time'] or 720) / 2. start_task(interval, self.appli.session_handler.clean_sessions) diff -r 986718a355fa -r 4d114865098f goa/appobjects/components.py --- a/goa/appobjects/components.py Thu Jul 23 12:55:51 2009 +0200 +++ b/goa/appobjects/components.py Thu Jul 23 15:33:03 2009 +0200 @@ -45,7 +45,7 @@ content_type = 'image/png' def call(self): """display global schema information""" - skipmeta = not int(self.req.form.get('withmeta', 0)) + skipmeta = int(self.req.form.get('skipmeta', 1)) if skipmeta: url = self.build_url('data/schema.png') else: @@ -72,7 +72,7 @@ continue etype = eschema.type label = display_name(req, etype, 'plural') - view = self.vreg.select_view('list', req, req.etype_rset(etype)) + view = self.vreg.select('views', 'list', req, req.etype_rset(etype)) url = view.url() etypelink = u' %s' % (xml_escape(url), label) yield (label, etypelink, self.add_entity_link(eschema, req)) diff -r 986718a355fa -r 4d114865098f goa/dbinit.py --- a/goa/dbinit.py Thu Jul 23 12:55:51 2009 +0200 +++ b/goa/dbinit.py Thu Jul 23 15:33:03 2009 +0200 @@ -86,14 +86,13 @@ def init_persistent_schema(ssession, schema): execute = ssession.unsafe_execute rql = ('INSERT CWEType X: X name %(name)s, X description %(descr)s,' - 'X final FALSE, X meta %(meta)s') + 'X final FALSE') eschema = schema.eschema('CWEType') - execute(rql, {'name': u'CWEType', 'descr': unicode(eschema.description), - 'meta': eschema.meta}) + execute(rql, {'name': u'CWEType', 'descr': unicode(eschema.description)}) for eschema in schema.entities(): if eschema.is_final() or eschema == 'CWEType': continue - execute(rql, {'name': unicode(eschema), 'meta': eschema.meta, + execute(rql, {'name': unicode(eschema), 'descr': unicode(eschema.description)}) def insert_versions(ssession, config): diff -r 986718a355fa -r 4d114865098f goa/test/unittest_editcontroller.py --- a/goa/test/unittest_editcontroller.py Thu Jul 23 12:55:51 2009 +0200 +++ b/goa/test/unittest_editcontroller.py Thu Jul 23 15:33:03 2009 +0200 @@ -37,8 +37,7 @@ self.ctrl = self.get_ctrl(self.req) def get_ctrl(self, req): - return self.vreg.select(self.vreg.registry_objects('controllers', 'edit'), - req=req, appli=self) + return self.vreg.select('controllers', 'edit', req=req, appli=self) def publish(self, req): assert req is self.ctrl.req diff -r 986718a355fa -r 4d114865098f goa/testlib.py --- a/goa/testlib.py Thu Jul 23 12:55:51 2009 +0200 +++ b/goa/testlib.py Thu Jul 23 15:33:03 2009 +0200 @@ -131,7 +131,7 @@ self.vreg.load_module(module) for cls in self.MODEL_CLASSES: self.vreg.load_object(cls) - self.session_manager = self.vreg.select_component('sessionmanager') + self.session_manager = self.vreg.select('components', 'sessionmanager') if need_ds_init: # create default groups and create entities according to the schema create_groups() diff -r 986718a355fa -r 4d114865098f goa/tools/generate_schema_img.py --- a/goa/tools/generate_schema_img.py Thu Jul 23 12:55:51 2009 +0200 +++ b/goa/tools/generate_schema_img.py Thu Jul 23 15:33:03 2009 +0200 @@ -8,6 +8,7 @@ import sys from os.path import dirname, abspath, join from yams import schema2dot +from cubicweb.web.views.schema import SKIP_TYPES APPLROOT = abspath(join(dirname(abspath(__file__)), '..')) @@ -22,9 +23,8 @@ skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of') path = join(APPLROOT, 'data', 'schema.png') schema2dot.schema2dot(schema, path, #size=size, - skiprels=skip_rels, skipmeta=True) + skiptypes=SKIP_TYPES) print 'generated', path path = join(APPLROOT, 'data', 'metaschema.png') -schema2dot.schema2dot(schema, path, #size=size, - skiprels=skip_rels, skipmeta=False) +schema2dot.schema2dot(schema, path) print 'generated', path diff -r 986718a355fa -r 4d114865098f goa/tools/laxctl.py --- a/goa/tools/laxctl.py Thu Jul 23 12:55:51 2009 +0200 +++ b/goa/tools/laxctl.py Thu Jul 23 15:33:03 2009 +0200 @@ -19,6 +19,8 @@ from logilab.common.clcommands import Command, register_commands, main_run from cubicweb.common.uilib import remove_html_tags +from cubicweb.web.views.schema import SKIP_TYPES + APPLROOT = osp.abspath(osp.join(osp.dirname(osp.abspath(__file__)), '..')) @@ -57,14 +59,12 @@ assert not args, 'no argument expected' from yams import schema2dot schema = self.vreg.schema - skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of') path = osp.join(APPLROOT, 'data', 'schema.png') schema2dot.schema2dot(schema, path, #size=size, - skiprels=skip_rels, skipmeta=True) + skiptypes=SKIP_TYPES) print 'generated', path path = osp.join(APPLROOT, 'data', 'metaschema.png') - schema2dot.schema2dot(schema, path, #size=size, - skiprels=skip_rels, skipmeta=False) + schema2dot.schema2dot(schema, path) print 'generated', path diff -r 986718a355fa -r 4d114865098f misc/migration/3.4.0_Any.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/migration/3.4.0_Any.py Thu Jul 23 15:33:03 2009 +0200 @@ -0,0 +1,2 @@ +drop_attribute('CWEType', 'meta') +drop_attribute('CWRType', 'meta') diff -r 986718a355fa -r 4d114865098f rset.py --- a/rset.py Thu Jul 23 12:55:51 2009 +0200 +++ b/rset.py Thu Jul 23 15:33:03 2009 +0200 @@ -83,7 +83,8 @@ try: return self._rsetactions[key] except KeyError: - actions = self.vreg.possible_vobjects('actions', self.req, self, **kwargs) + actions = self.vreg.possible_vobjects('actions', self.req, + rset=self, **kwargs) self._rsetactions[key] = actions return actions diff -r 986718a355fa -r 4d114865098f schema.py --- a/schema.py Thu Jul 23 12:55:51 2009 +0200 +++ b/schema.py Thu Jul 23 15:33:03 2009 +0200 @@ -20,8 +20,8 @@ from yams import BadSchemaDefinition, buildobjs as ybo from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema from yams.constraints import BaseConstraint, StaticVocabularyConstraint -from yams.reader import (CONSTRAINTS, RelationFileReader, PyFileReader, - SchemaLoader) +from yams.reader import CONSTRAINTS, PyFileReader, SchemaLoader, \ + obsolete as yobsolete from rql import parse, nodes, RQLSyntaxError, TypeResolverException @@ -33,7 +33,19 @@ schema.use_py_datetime() nodes.use_py_datetime() -BASEGROUPS = ('managers', 'users', 'guests', 'owners') +# set of meta-relations available for every entity types +META_RELATIONS_TYPES = set(( + 'owned_by', 'created_by', 'is', 'is_instance_of', 'identity', + 'eid', 'creation_date', 'modification_date', 'has_text', 'cwuri', + )) + +# set of entity and relation types used to build the schema +SCHEMA_TYPES = set(( + 'CWEType', 'CWRType', 'CWAttribute', 'CWRelation', + 'CWConstraint', 'CWConstraintType', 'RQLExpression', + 'relation_type', 'from_entity', 'to_entity', + 'constrained_by', 'cstrtype', + )) _LOGGER = getLogger('cubicweb.schemaloader') @@ -50,31 +62,6 @@ etype = ETYPE_NAME_MAP[etype] return etype -# monkey path yams.builder.RelationDefinition to support a new wildcard type '@' -# corresponding to system entity (ie meta but not schema) -def _actual_types(self, schema, etype): - # two bits of error checking & reporting : - if type(etype) not in (str, list, tuple): - raise RuntimeError, ('Entity types must not be instances but strings or' - ' list/tuples thereof. Ex. (bad, good) : ' - 'SubjectRelation(Foo), SubjectRelation("Foo"). ' - 'Hence, %r is not acceptable.' % etype) - # real work : - if etype == '**': - return self._pow_etypes(schema) - if isinstance(etype, (tuple, list)): - return etype - if '*' in etype or '@' in etype: - assert len(etype) in (1, 2) - etypes = () - if '*' in etype: - etypes += tuple(self._wildcard_etypes(schema)) - if '@' in etype: - etypes += tuple(system_etypes(schema)) - return etypes - return (etype,) -ybo.RelationDefinition._actual_types = _actual_types - ## cubicweb provides a RichString class for convenience class RichString(ybo.String): @@ -87,8 +74,8 @@ is equivalent to:: class Card(EntityType): - content_format = String(meta=True, internationalizable=True, - default='text/rest', constraints=[format_constraint]) + content_format = String(internationalizable=True, + default='text/rest', constraints=[format_constraint]) content = String(fulltextindexed=True) """ def __init__(self, default_format='text/plain', format_constraints=None, **kwargs): @@ -96,28 +83,28 @@ self.format_constraints = format_constraints or [format_constraint] super(RichString, self).__init__(**kwargs) -PyFileReader.context['RichString'] = RichString +PyFileReader.context['RichString'] = yobsolete(RichString) ## need to monkeypatch yams' _add_relation function to handle RichString yams_add_relation = ybo._add_relation @monkeypatch(ybo) def _add_relation(relations, rdef, name=None, insertidx=None): if isinstance(rdef, RichString): - format_attrdef = ybo.String(meta=True, internationalizable=True, + format_attrdef = ybo.String(internationalizable=True, default=rdef.default_format, maxsize=50, constraints=rdef.format_constraints) 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: +@monkeypatch(ybo.EntityType, methodname='add_relation') +@classmethod +def add_relation(cls, rdef, name=None): + ybo.add_relation_function(cls, rdef, name) + if isinstance(rdef, RichString) and not rdef in cls._defined: format_attr_name = (name or rdef.name) + '_format' - rdef = self.get_relations(format_attr_name).next() - self._ensure_relation_type(rdef) + rdef = cls.get_relations(format_attr_name).next() + cls._ensure_relation_type(rdef) def display_name(req, key, form=''): """return a internationalized string for the key (schema entity or relation @@ -253,7 +240,7 @@ """return system entity types only: skip final, schema and application entities """ for eschema in schema.entities(): - if eschema.is_final() or eschema.schema_entity() or not eschema.meta: + if eschema.is_final() or eschema.schema_entity(): continue yield eschema.type @@ -293,6 +280,15 @@ continue yield rschema, attrschema + def main_attribute(self): + """convenience method that returns the *main* (i.e. the first non meta) + attribute defined in the entity schema + """ + for rschema, _ in self.attribute_definitions(): + if not (rschema in META_RELATIONS_TYPES + or self.is_metadata(rschema)): + return rschema + def add_subject_relation(self, rschema): """register the relation schema as possible subject relation""" super(CubicWebEntitySchema, self).add_subject_relation(rschema) @@ -332,7 +328,7 @@ def schema_entity(self): """return True if this entity type is used to build the schema""" - return self.type in self.schema.schema_entity_types() + return self.type in SCHEMA_TYPES def check_perm(self, session, action, eid=None): # NB: session may be a server session or a request object @@ -370,6 +366,9 @@ eid = getattr(rdef, 'eid', None) self.eid = eid + @property + def meta(self): + return self.type in META_RELATIONS_TYPES def update(self, subjschema, objschema, rdef): super(CubicWebRelationSchema, self).update(subjschema, objschema, rdef) @@ -408,8 +407,8 @@ (target == 'object' and card[1]) def schema_relation(self): - return self.type in ('relation_type', 'from_entity', 'to_entity', - 'constrained_by', 'cstrtype') + """return True if this relation type is used to build the schema""" + return self.type in SCHEMA_TYPES def physical_mode(self): """return an appropriate mode for physical storage of this relation type: @@ -459,24 +458,16 @@ self._eid_index = {} super(CubicWebSchema, self).__init__(*args, **kwargs) ybo.register_base_types(self) - rschema = self.add_relation_type(ybo.RelationType('eid', meta=True)) + rschema = self.add_relation_type(ybo.RelationType('eid')) rschema.final = True rschema.set_default_groups() - rschema = self.add_relation_type(ybo.RelationType('has_text', meta=True)) + rschema = self.add_relation_type(ybo.RelationType('has_text')) rschema.final = True rschema.set_default_groups() - rschema = self.add_relation_type(ybo.RelationType('identity', meta=True)) + rschema = self.add_relation_type(ybo.RelationType('identity')) rschema.final = False rschema.set_default_groups() - def schema_entity_types(self): - """return the list of entity types used to build the schema""" - return frozenset(('CWEType', 'CWRType', 'CWAttribute', 'CWRelation', - 'CWConstraint', 'CWConstraintType', 'RQLExpression', - # XXX those are not really "schema" entity types - # but we usually don't want them as @* targets - 'CWProperty', 'CWPermission', 'State', 'Transition')) - def add_entity_type(self, edef): edef.name = edef.name.encode() edef.name = bw_normalize_etype(edef.name) @@ -799,7 +790,7 @@ return self._check(session, x=eid) return self._check(session) -PyFileReader.context['ERQLExpression'] = ERQLExpression +PyFileReader.context['ERQLExpression'] = yobsolete(ERQLExpression) class RRQLExpression(RQLExpression): def __init__(self, expression, mainvars=None, eid=None): @@ -843,7 +834,7 @@ kwargs['o'] = toeid return self._check(session, **kwargs) -PyFileReader.context['RRQLExpression'] = RRQLExpression +PyFileReader.context['RRQLExpression'] = yobsolete(RRQLExpression) # workflow extensions ######################################################### @@ -875,23 +866,6 @@ # schema loading ############################################################## -class CubicWebRelationFileReader(RelationFileReader): - """cubicweb specific relation file reader, handling additional RQL - constraints on a relation definition - """ - - def handle_constraint(self, rdef, constraint_text): - """arbitrary constraint is an rql expression for cubicweb""" - if not rdef.constraints: - rdef.constraints = [] - rdef.constraints.append(RQLVocabularyConstraint(constraint_text)) - - def process_properties(self, rdef, relation_def): - if 'inline' in relation_def: - rdef.inlined = True - RelationFileReader.process_properties(self, rdef, relation_def) - - CONSTRAINTS['RQLConstraint'] = RQLConstraint CONSTRAINTS['RQLUniqueConstraint'] = RQLUniqueConstraint CONSTRAINTS['RQLVocabularyConstraint'] = RQLVocabularyConstraint @@ -903,8 +877,6 @@ the persistent schema """ schemacls = CubicWebSchema - SchemaLoader.file_handlers.update({'.rel' : CubicWebRelationFileReader, - }) def load(self, config, path=(), **kwargs): """return a Schema instance from the schema definition read diff -r 986718a355fa -r 4d114865098f schemas/Bookmark.py --- a/schemas/Bookmark.py Thu Jul 23 12:55:51 2009 +0200 +++ b/schemas/Bookmark.py Thu Jul 23 15:33:03 2009 +0200 @@ -5,9 +5,21 @@ :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses """ +__docformat__ = "restructuredtext en" +_ = unicode -class Bookmark(MetaUserEntityType): +from yams.buildobjs import EntityType, RelationType, SubjectRelation, String +from cubicweb.schema import RRQLExpression + +class Bookmark(EntityType): """bookmarks are used to have user's specific internal links""" + permissions = { + 'read': ('managers', 'users', 'guests',), + 'add': ('managers', 'users',), + 'delete': ('managers', 'owners',), + 'update': ('managers', 'owners',), + } + title = String(required=True, maxsize=128, internationalizable=True) path = String(maxsize=512, required=True, description=_("relative url of the bookmarked page")) @@ -16,7 +28,7 @@ description=_("users using this bookmark")) -class bookmarked_by(MetaUserRelationType): +class bookmarked_by(RelationType): permissions = {'read': ('managers', 'users', 'guests',), # test user in users group to avoid granting permission to anonymous user 'add': ('managers', RRQLExpression('O identity U, U in_group G, G name "users"')), diff -r 986718a355fa -r 4d114865098f schemas/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/schemas/__init__.py Thu Jul 23 15:33:03 2009 +0200 @@ -0,0 +1,13 @@ + +META_ETYPE_PERMS = { + 'read': ('managers', 'users', 'guests',), + 'add': ('managers',), + 'delete': ('managers',), + 'update': ('managers', 'owners',), + } + +META_RTYPE_PERMS = { + 'read': ('managers', 'users', 'guests',), + 'add': ('managers',), + 'delete': ('managers',), + } diff -r 986718a355fa -r 4d114865098f schemas/base.py --- a/schemas/base.py Thu Jul 23 12:55:51 2009 +0200 +++ b/schemas/base.py Thu Jul 23 15:33:03 2009 +0200 @@ -6,11 +6,16 @@ :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses """ __docformat__ = "restructuredtext en" +_ = unicode +from yams.buildobjs import (EntityType, RelationType, SubjectRelation, + String, Boolean, Datetime, Password) +from cubicweb.schema import (RQLConstraint, WorkflowableEntityType, + ERQLExpression, RRQLExpression) +from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS class CWUser(WorkflowableEntityType): """define a CubicWeb user""" - meta = True # XXX backported from old times, shouldn't be there anymore permissions = { 'read': ('managers', 'users', ERQLExpression('X identity U')), 'add': ('managers',), @@ -35,7 +40,7 @@ description=_('groups grant permissions to the user')) -class EmailAddress(MetaEntityType): +class EmailAddress(EntityType): """an electronic mail address associated to a short alias""" permissions = { 'read': ('managers', 'users', 'guests',), # XXX if P use_email X, U has_read_permission P @@ -81,11 +86,11 @@ 'delete': ('managers', RRQLExpression('U has_update_permission S'),), } -class in_group(MetaRelationType): +class in_group(RelationType): """core relation indicating a user's groups""" - meta = False + permissions = META_RTYPE_PERMS -class owned_by(MetaRelationType): +class owned_by(RelationType): """core relation indicating owners of an entity. This relation implicitly put the owner into the owners group for the entity """ @@ -97,10 +102,10 @@ # 0..n cardinality for entities created by internal session (no attached user) # and to support later deletion of a user which has created some entities cardinality = '**' - subject = '**' + subject = '*' object = 'CWUser' -class created_by(MetaRelationType): +class created_by(RelationType): """core relation indicating the original creator of an entity""" permissions = { 'read': ('managers', 'users', 'guests'), @@ -110,21 +115,27 @@ # 0..1 cardinality for entities created by internal session (no attached user) # and to support later deletion of a user which has created some entities cardinality = '?*' - subject = '**' + subject = '*' object = 'CWUser' -class creation_date(MetaAttributeRelationType): +class creation_date(RelationType): """creation time of an entity""" cardinality = '11' - subject = '**' + subject = '*' object = 'Datetime' -class modification_date(MetaAttributeRelationType): +class modification_date(RelationType): """latest modification time of an entity""" cardinality = '11' + subject = '*' + object = 'Datetime' + +class cwuri(RelationType): + """internal entity uri""" + cardinality = '11' subject = '**' - object = 'Datetime' + object = 'String' class CWProperty(EntityType): @@ -137,7 +148,6 @@ 'update': ('managers', 'owners',), 'delete': ('managers', 'owners',), } - meta = True # key is a reserved word for mysql pkey = String(required=True, internationalizable=True, maxsize=256, description=_('defines what\'s the property is applied for. ' @@ -152,7 +162,7 @@ ' a global property')) -class for_user(MetaRelationType): +class for_user(RelationType): """link a property to the user which want this property customization. Unless you're a site manager, this relation will be handled automatically. """ @@ -164,9 +174,11 @@ inlined = True -class CWPermission(MetaEntityType): +class CWPermission(EntityType): """entity type that may be used to construct some advanced security configuration """ + permissions = META_ETYPE_PERMS + name = String(required=True, indexed=True, internationalizable=True, maxsize=100, description=_('name or identifier of the permission')) label = String(required=True, internationalizable=True, maxsize=100, @@ -186,7 +198,7 @@ 'delete': ('managers',), } -class require_group(MetaRelationType): +class require_group(RelationType): """used to grant a permission to a group""" permissions = { 'read': ('managers', 'users', 'guests'), @@ -199,8 +211,31 @@ """generic relation to link one entity to another""" symetric = True +class ExternalUri(EntityType): + """a URI representing an object in external data store""" + uri = String(required=True, unique=True, maxsize=256, + description=_('the URI of the object')) -class CWCache(MetaEntityType): +class same_as(RelationType): + """generic relation to specify that an external entity represent the same + object as a local one: + http://www.w3.org/TR/owl-ref/#sameAs-def + + NOTE: You'll have to explicitly declare which entity types can have a + same_as relation + """ + permissions = { + 'read': ('managers', 'users', 'guests',), + 'add': ('managers', 'users'), + 'delete': ('managers', 'owners'), + } + cardinality = '*1' + symetric = True + # NOTE: the 'object = ExternalUri' declaration will still be mandatory + # in the cube's schema. + object = 'ExternalUri' + +class CWCache(EntityType): """a simple cache entity characterized by a name and a validity date. @@ -212,7 +247,7 @@ permissions = { 'read': ('managers', 'users', 'guests'), 'add': ('managers',), - 'update': ('managers', 'users',), + 'update': ('managers', 'users',), # XXX 'delete': ('managers',), } diff -r 986718a355fa -r 4d114865098f schemas/bootstrap.py --- a/schemas/bootstrap.py Thu Jul 23 12:55:51 2009 +0200 +++ b/schemas/bootstrap.py Thu Jul 23 15:33:03 2009 +0200 @@ -5,32 +5,34 @@ :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses """ +__docformat__ = "restructuredtext en" +_ = unicode -from cubicweb.schema import format_constraint - +from yams.buildobjs import (EntityType, RelationType, SubjectRelation, + ObjectRelation, String, Boolean, Int) +from cubicweb.schema import RichString, RQLConstraint +from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS # not restricted since as "is" is handled as other relations, guests need # access to this -class CWEType(MetaEntityType): +class CWEType(EntityType): """define an entity type, used to build the application schema""" + permissions = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) description = RichString(internationalizable=True, description=_('semantic description of this entity type')) - meta = Boolean(description=_('is it an application entity type or not ?')) # necessary to filter using RQL final = Boolean(description=_('automatic')) -class CWRType(MetaEntityType): +class CWRType(EntityType): """define a relation type, used to build the application schema""" + permissions = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) - description_format = String(meta=True, internationalizable=True, maxsize=50, - default='text/plain', constraints=[format_constraint]) - description = String(internationalizable=True, - description=_('semantic description of this relation type')) - meta = Boolean(description=_('is it an application relation type or not ?')) + description = RichString(internationalizable=True, + description=_('semantic description of this relation type')) symetric = Boolean(description=_('is this relation equivalent in both direction ?')) inlined = Boolean(description=_('is this relation physically inlined? you should know what you\'re doing if you are changing this!')) fulltext_container = String(description=_('if full text content of subject/object entity ' @@ -40,12 +42,13 @@ final = Boolean(description=_('automatic')) -class CWAttribute(MetaEntityType): +class CWAttribute(EntityType): """define a final relation: link a final relation type from a non final entity to a final entity type. used to build the application schema """ + permissions = META_ETYPE_PERMS relation_type = SubjectRelation('CWRType', cardinality='1*', constraints=[RQLConstraint('O final TRUE')], composite='object') @@ -67,10 +70,8 @@ internationalizable = Boolean(description=_('is this attribute\'s value translatable')) defaultval = String(maxsize=256) - description_format = String(meta=True, internationalizable=True, maxsize=50, - default='text/plain', constraints=[format_constraint]) - description = String(internationalizable=True, - description=_('semantic description of this attribute')) + description = RichString(internationalizable=True, + description=_('semantic description of this attribute')) CARDINALITY_VOCAB = [_('?*'), _('1*'), _('+*'), _('**'), @@ -78,12 +79,13 @@ _('?1'), _('11'), _('+1'), _('*1'), _('??'), _('1?'), _('+?'), _('*?')] -class CWRelation(MetaEntityType): +class CWRelation(EntityType): """define a non final relation: link a non final relation type from a non final entity to a non final entity type. used to build the application schema """ + permissions = META_ETYPE_PERMS relation_type = SubjectRelation('CWRType', cardinality='1*', constraints=[RQLConstraint('O final FALSE')], composite='object') @@ -107,15 +109,14 @@ vocabulary=('', _('subject'), _('object')), maxsize=8, default=None) - description_format = String(meta=True, internationalizable=True, maxsize=50, - default='text/plain', constraints=[format_constraint]) - description = String(internationalizable=True, - description=_('semantic description of this relation')) + description = RichString(internationalizable=True, + description=_('semantic description of this relation')) # not restricted since it has to be read when checking allowed transitions -class RQLExpression(MetaEntityType): +class RQLExpression(EntityType): """define a rql expression used to define permissions""" + permissions = META_ETYPE_PERMS exprtype = String(required=True, vocabulary=['ERQLExpression', 'RRQLExpression']) mainvars = String(maxsize=8, description=_('name of the main variables which should be ' @@ -140,21 +141,24 @@ description=_('rql expression allowing to update entities of this type')) -class CWConstraint(MetaEntityType): +class CWConstraint(EntityType): """define a schema constraint""" + permissions = META_ETYPE_PERMS cstrtype = SubjectRelation('CWConstraintType', cardinality='1*') value = String(description=_('depends on the constraint type')) -class CWConstraintType(MetaEntityType): +class CWConstraintType(EntityType): """define a schema constraint type""" + permissions = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) # not restricted since it has to be read when checking allowed transitions -class CWGroup(MetaEntityType): +class CWGroup(EntityType): """define a CubicWeb users group""" + permissions = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, unique=True, maxsize=64) @@ -169,40 +173,55 @@ -class relation_type(MetaRelationType): +class relation_type(RelationType): """link a relation definition to its relation type""" - inlined = True -class from_entity(MetaRelationType): - """link a relation definition to its subject entity type""" + permissions = META_RTYPE_PERMS inlined = True -class to_entity(MetaRelationType): - """link a relation definition to its object entity type""" - inlined = True -class constrained_by(MetaRelationType): - """constraints applying on this relation""" -class cstrtype(MetaRelationType): - """constraint factory""" +class from_entity(RelationType): + """link a relation definition to its subject entity type""" + permissions = META_RTYPE_PERMS inlined = True -class read_permission(MetaRelationType): +class to_entity(RelationType): + """link a relation definition to its object entity type""" + permissions = META_RTYPE_PERMS + inlined = True + +class constrained_by(RelationType): + """constraints applying on this relation""" + permissions = META_RTYPE_PERMS + +class cstrtype(RelationType): + """constraint factory""" + permissions = META_RTYPE_PERMS + inlined = True + +class read_permission(RelationType): """core relation giving to a group the permission to read an entity or relation type """ -class add_permission(MetaRelationType): + permissions = META_RTYPE_PERMS + +class add_permission(RelationType): """core relation giving to a group the permission to add an entity or relation type """ -class delete_permission(MetaRelationType): + permissions = META_RTYPE_PERMS + +class delete_permission(RelationType): """core relation giving to a group the permission to delete an entity or relation type """ -class update_permission(MetaRelationType): + permissions = META_RTYPE_PERMS + +class update_permission(RelationType): """core relation giving to a group the permission to update an entity type """ + permissions = META_RTYPE_PERMS -class is_(MetaRelationType): +class is_(RelationType): """core relation indicating the type of an entity """ name = 'is' @@ -214,10 +233,10 @@ 'delete': (), } cardinality = '1*' - subject = '**' + subject = '*' object = 'CWEType' -class is_instance_of(MetaRelationType): +class is_instance_of(RelationType): """core relation indicating the types (including specialized types) of an entity """ @@ -229,10 +248,10 @@ 'delete': (), } cardinality = '+*' - subject = '**' + subject = '*' object = 'CWEType' -class specializes(MetaRelationType): +class specializes(RelationType): name = 'specializes' permissions = { 'read': ('managers', 'users', 'guests'), diff -r 986718a355fa -r 4d114865098f schemas/workflow.py --- a/schemas/workflow.py Thu Jul 23 12:55:51 2009 +0200 +++ b/schemas/workflow.py Thu Jul 23 15:33:03 2009 +0200 @@ -5,11 +5,20 @@ :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses """ +__docformat__ = "restructuredtext en" +_ = unicode -class State(MetaEntityType): +from yams.buildobjs import (EntityType, RelationType, SubjectRelation, + ObjectRelation, String) +from cubicweb.schema import RichString, RQLConstraint +from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS + +class State(EntityType): """used to associate simple states to an entity type and/or to define workflows """ + permissions = META_ETYPE_PERMS + name = String(required=True, indexed=True, internationalizable=True, maxsize=256) description = RichString(fulltextindexed=True, default_format='text/rest', @@ -28,15 +37,15 @@ description=_('initial state for entities of this type')) -class Transition(MetaEntityType): +class Transition(EntityType): """use to define a transition from one or multiple states to a destination states in workflow's definitions. """ + permissions = META_ETYPE_PERMS + name = String(required=True, indexed=True, internationalizable=True, maxsize=256) - description_format = String(meta=True, internationalizable=True, maxsize=50, - default='text/rest', constraints=[format_constraint]) - description = String(fulltextindexed=True, + description = RichString(fulltextindexed=True, description=_('semantic description of this transition')) condition = SubjectRelation('RQLExpression', cardinality='*?', composite='subject', description=_('a RQL expression which should return some results, ' @@ -56,20 +65,22 @@ description=_('destination state for this transition')) -class TrInfo(MetaEntityType): +class TrInfo(EntityType): + permissions = META_ETYPE_PERMS + from_state = SubjectRelation('State', cardinality='?*') to_state = SubjectRelation('State', cardinality='1*') - comment_format = String(meta=True, internationalizable=True, maxsize=50, - default='text/rest', constraints=[format_constraint]) - comment = String(fulltextindexed=True) + comment = RichString(fulltextindexed=True) # get actor and date time using owned_by and creation_date -class from_state(MetaRelationType): +class from_state(RelationType): + permissions = META_RTYPE_PERMS inlined = True -class to_state(MetaRelationType): +class to_state(RelationType): + permissions = META_RTYPE_PERMS inlined = True -class wf_info_for(MetaRelationType): +class wf_info_for(RelationType): """link a transition information to its object""" permissions = { 'read': ('managers', 'users', 'guests',),# RRQLExpression('U has_read_permission O')), @@ -80,30 +91,39 @@ composite = 'object' fulltext_container = composite -class state_of(MetaRelationType): +class state_of(RelationType): """link a state to one or more entity type""" -class transition_of(MetaRelationType): + permissions = META_RTYPE_PERMS +class transition_of(RelationType): """link a transition to one or more entity type""" + permissions = META_RTYPE_PERMS -class initial_state(MetaRelationType): +class initial_state(RelationType): """indicate which state should be used by default when an entity using states is created """ - inlined = True - -class destination_state(MetaRelationType): - """destination state of a transition""" + permissions = META_RTYPE_PERMS inlined = True -class allowed_transition(MetaRelationType): - """allowed transition from this state""" +class destination_state(RelationType): + """destination state of a transition""" + permissions = META_RTYPE_PERMS + inlined = True -class in_state(UserRelationType): +class allowed_transition(RelationType): + """allowed transition from this state""" + permissions = META_RTYPE_PERMS + +class in_state(RelationType): """indicate the current state of an entity""" - meta = True # not inlined intentionnaly since when using ldap sources, user'state # has to be stored outside the CWUser table # add/delete perms given to managers/users, after what most of the job # is done by workflow enforcment + permissions = { + 'read': ('managers', 'users', 'guests',), + 'add': ('managers', 'users',), # XXX has_update_perm + 'delete': ('managers', 'users',), + } diff -r 986718a355fa -r 4d114865098f schemaviewer.py --- a/schemaviewer.py Thu Jul 23 12:55:51 2009 +0200 +++ b/schemaviewer.py Thu Jul 23 15:33:03 2009 +0200 @@ -6,11 +6,11 @@ :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses """ __docformat__ = "restructuredtext en" +_ = unicode from logilab.common.ureports import Section, Title, Table, Link, Span, Text from yams.schema2dot import CARD_MAP -_ = unicode I18NSTRINGS = [_('read'), _('add'), _('delete'), _('update'), _('order')] class SchemaViewer(object): @@ -38,8 +38,7 @@ klass='acl') - def visit_schema(self, schema, display_relations=0, - skiprels=(), skipmeta=True): + def visit_schema(self, schema, display_relations=0, skiptypes=()): """get a layout for a whole schema""" title = Title(self.req._('Schema %s') % schema.name, klass='titleUnderline') @@ -48,21 +47,15 @@ klass='titleUnderline'),)) layout.append(esection) eschemas = [eschema for eschema in schema.entities() - if not eschema.is_final()] - if skipmeta: - eschemas = [eschema for eschema in eschemas - if not eschema.meta] + if not (eschema.is_final() or eschema in skiptypes)] for eschema in sorted(eschemas): - esection.append(self.visit_entityschema(eschema, skiprels)) + esection.append(self.visit_entityschema(eschema, skiptypes)) if display_relations: title = Title(self.req._('Relations'), klass='titleUnderline') rsection = Section(children=(title,)) layout.append(rsection) relations = [rschema for rschema in schema.relations() - if not (rschema.is_final() or rschema.type in skiprels)] - if skipmeta: - relations = [rschema for rschema in relations - if not rschema.meta] + if not (rschema.is_final() or rschema.type in skiptypes)] keys = [(rschema.type, rschema) for rschema in relations] for key, rschema in sorted(keys): relstr = self.visit_relationschema(rschema) @@ -107,17 +100,13 @@ def stereotype(self, name): return Span((' <<%s>>' % name,), klass='stereotype') - def visit_entityschema(self, eschema, skiprels=()): + def visit_entityschema(self, eschema, skiptypes=()): """get a layout for an entity schema""" etype = eschema.type layout = Section(children=' ', klass='clear') layout.append(Link(etype,' ' , id=etype)) # anchor title = Link(self.eschema_link_url(eschema), etype) - if eschema.meta: - stereotype = self.stereotype('meta') - boxchild = [Section(children=(title, ' (%s)'%eschema.display_name(self.req), stereotype), klass='title')] - else: - boxchild = [Section(children=(title, ' (%s)'%eschema.display_name(self.req)), klass='title')] + boxchild = [Section(children=(title, ' (%s)'% eschema.display_name(self.req)), klass='title')] table = Table(cols=4, rheaders=1, children=self._entity_attributes_data(eschema)) boxchild.append(Section(children=(table,), klass='body')) @@ -129,7 +118,7 @@ rels = [] first = True for rschema, targetschemas, x in eschema.relation_definitions(): - if rschema.type in skiprels: + if rschema.type in skiptypes: continue if not (rschema.has_local_role('read') or rschema.has_perm(self.req, 'read')): continue diff -r 986718a355fa -r 4d114865098f selectors.py --- a/selectors.py Thu Jul 23 12:55:51 2009 +0200 +++ b/selectors.py Thu Jul 23 15:33:03 2009 +0200 @@ -287,7 +287,7 @@ @objectify_selector @lltrace -def none_rset(cls, req, rset=None, *args, **kwargs): +def none_rset(cls, req, rset=None, **kwargs): """accept no result set (e.g. given rset is None)""" if rset is None: return 1 @@ -295,7 +295,7 @@ @objectify_selector @lltrace -def any_rset(cls, req, rset=None, *args, **kwargs): +def any_rset(cls, req, rset=None, **kwargs): """accept result set, whatever the number of result it contains""" if rset is not None: return 1 @@ -303,7 +303,7 @@ @objectify_selector @lltrace -def nonempty_rset(cls, req, rset=None, *args, **kwargs): +def nonempty_rset(cls, req, rset=None, **kwargs): """accept any non empty result set""" if rset is not None and rset.rowcount: return 1 @@ -311,7 +311,7 @@ @objectify_selector @lltrace -def empty_rset(cls, req, rset=None, *args, **kwargs): +def empty_rset(cls, req, rset=None, **kwargs): """accept empty result set""" if rset is not None and rset.rowcount == 0: return 1 @@ -319,7 +319,7 @@ @objectify_selector @lltrace -def one_line_rset(cls, req, rset=None, row=None, *args, **kwargs): +def one_line_rset(cls, req, rset=None, row=None, **kwargs): """if row is specified, accept result set with a single line of result, else accepts anyway """ @@ -329,7 +329,7 @@ @objectify_selector @lltrace -def two_lines_rset(cls, req, rset=None, *args, **kwargs): +def two_lines_rset(cls, req, rset=None, **kwargs): """accept result set with *at least* two lines of result""" if rset is not None and rset.rowcount > 1: return 1 @@ -337,7 +337,7 @@ @objectify_selector @lltrace -def two_cols_rset(cls, req, rset=None, *args, **kwargs): +def two_cols_rset(cls, req, rset=None, **kwargs): """accept result set with at least one line and two columns of result""" if rset is not None and rset.rowcount and len(rset.rows[0]) > 1: return 1 @@ -345,7 +345,7 @@ @objectify_selector @lltrace -def paginated_rset(cls, req, rset=None, *args, **kwargs): +def paginated_rset(cls, req, rset=None, **kwargs): """accept result set with more lines than the page size. Page size is searched in (respecting order): @@ -366,7 +366,7 @@ @objectify_selector @lltrace -def sorted_rset(cls, req, rset=None, row=None, col=0, **kwargs): +def sorted_rset(cls, req, rset=None, **kwargs): """accept sorted result set""" rqlst = rset.syntax_tree() if len(rqlst.children) > 1 or not rqlst.children[0].orderby: @@ -375,7 +375,7 @@ @objectify_selector @lltrace -def one_etype_rset(cls, req, rset=None, row=None, col=0, *args, **kwargs): +def one_etype_rset(cls, req, rset=None, col=0, **kwargs): """accept result set where entities in the specified column (or 0) are all of the same type """ @@ -387,7 +387,7 @@ @objectify_selector @lltrace -def two_etypes_rset(cls, req, rset=None, row=None, col=0, **kwargs): +def two_etypes_rset(cls, req, rset=None, col=0, **kwargs): """accept result set where entities in the specified column (or 0) are not of the same type """ @@ -572,7 +572,7 @@ def __call__(self, cls, req, rset=None, *args, **kwargs): try: - cls.vreg.select_object(self.registry, self.oid, req, rset, *args, **kwargs) + cls.vreg.select(self.registry, self.oid, req, rset=rset, **kwargs) return 1 except NoSelectableObject: return 0 @@ -942,6 +942,7 @@ def __repr__(self): return u'' % (self.rql, id(self)) + class but_etype(EntitySelector): """accept if the given entity types are not found in the result set. diff -r 986718a355fa -r 4d114865098f server/__init__.py --- a/server/__init__.py Thu Jul 23 12:55:51 2009 +0200 +++ b/server/__init__.py Thu Jul 23 15:33:03 2009 +0200 @@ -24,7 +24,7 @@ a initial user) """ from glob import glob - from cubicweb.schema import BASEGROUPS + from yams import BASE_GROUPS from cubicweb.dbapi import in_memory_cnx from cubicweb.server.repository import Repository from cubicweb.server.utils import manager_userpasswd @@ -93,7 +93,7 @@ login, pwd = unicode(source['db-user']), source['db-password'] print '-> inserting default user and default groups.' needisfix = [] - for group in BASEGROUPS: + for group in BASE_GROUPS: rset = session.execute('INSERT CWGroup X: X name %(name)s', {'name': unicode(group)}) needisfix.append( (rset.rows[0][0], rset.description[0][0]) ) diff -r 986718a355fa -r 4d114865098f server/hooks.py --- a/server/hooks.py Thu Jul 23 12:55:51 2009 +0200 +++ b/server/hooks.py Thu Jul 23 15:33:03 2009 +0200 @@ -33,6 +33,8 @@ entity['creation_date'] = datetime.now() if not 'modification_date' in entity: entity['modification_date'] = datetime.now() + if not 'cwuri' in entity: + entity['cwuri'] = session.base_url() + u'eid/%s' % entity.eid def setmtime_before_update_entity(session, entity): """update an entity -> set modification date""" diff -r 986718a355fa -r 4d114865098f server/hooksmanager.py --- a/server/hooksmanager.py Thu Jul 23 12:55:51 2009 +0200 +++ b/server/hooksmanager.py Thu Jul 23 15:33:03 2009 +0200 @@ -251,7 +251,7 @@ raise NotImplementedError class SystemHook(Hook): - accepts = ('',) + accepts = () from logging import getLogger from cubicweb import set_log_methods diff -r 986718a355fa -r 4d114865098f server/schemahooks.py --- a/server/schemahooks.py Thu Jul 23 12:55:51 2009 +0200 +++ b/server/schemahooks.py Thu Jul 23 15:33:03 2009 +0200 @@ -26,7 +26,7 @@ # core entity and relation types which can't be removed CORE_ETYPES = list(BASE_TYPES) + ['CWEType', 'CWRType', 'CWUser', 'CWGroup', 'CWConstraint', 'CWAttribute', 'CWRelation'] -CORE_RTYPES = ['eid', 'creation_date', 'modification_date', +CORE_RTYPES = ['eid', 'creation_date', 'modification_date', 'cwuri', 'login', 'upassword', 'name', 'is', 'instanceof', 'owned_by', 'created_by', 'in_group', 'relation_type', 'from_entity', 'to_entity', @@ -189,7 +189,7 @@ DeleteCWRTypeOp(session, name) -class DelRelationDefOp(SchemaOperation): +class DeleteRelationDefOp(SchemaOperation): """actually remove the relation definition from the application's schema""" def commit_event(self): subjtype, rtype, objtype = self.kobj @@ -238,7 +238,7 @@ # if this is the last instance, drop associated relation type if lastrel and not rteid in pendings: execute('DELETE CWRType X WHERE X eid %(x)s', {'x': rteid}, 'x') - DelRelationDefOp(session, (subjschema, rschema, objschema)) + DeleteRelationDefOp(session, (subjschema, rschema, objschema)) # addition #################################################################### @@ -283,7 +283,7 @@ prefix=SQL_PREFIX) relrqls = [] for rtype in ('is', 'is_instance_of', 'creation_date', 'modification_date', - 'created_by', 'owned_by'): + 'cwuri', 'created_by', 'owned_by'): rschema = schema[rtype] sampletype = rschema.subjects()[0] desttype = rschema.objects()[0] @@ -883,7 +883,7 @@ self.perm, erschema.type, self.group) -class DelRQLExpressionPermissionOp(AddRQLExpressionPermissionOp): +class DeleteRQLExpressionPermissionOp(AddRQLExpressionPermissionOp): """synchronize schema when a *_permission relation has been deleted from an rql expression""" def commit_event(self): @@ -919,7 +919,7 @@ else: # RQLExpression expr = session.execute('Any EXPR WHERE X eid %(x)s, X expression EXPR', {'x': object}, 'x')[0][0] - DelRQLExpressionPermissionOp(session, perm, subject, expr) + DeleteRQLExpressionPermissionOp(session, perm, subject, expr) def rebuild_infered_relations(session, subject, rtype, object): diff -r 986718a355fa -r 4d114865098f server/schemaserial.py --- a/server/schemaserial.py Thu Jul 23 12:55:51 2009 +0200 +++ b/server/schemaserial.py Thu Jul 23 15:33:03 2009 +0200 @@ -114,10 +114,10 @@ index = {} permsdict = deserialize_ertype_permissions(session) schema.reading_from_database = True - for eid, etype, desc, meta in session.execute('Any X, N, D, M WHERE ' - 'X is CWEType, X name N, ' - 'X description D, X meta M', - build_descr=False): + for eid, etype, desc in session.execute('Any X, N, D WHERE ' + 'X is CWEType, X name N, ' + 'X description D', + build_descr=False): # base types are already in the schema, skip them if etype in schemamod.BASE_TYPES: # just set the eid @@ -152,7 +152,7 @@ repo.clear_caches(tocleanup) session.commit(False) etype = netype - etype = ybo.EntityType(name=etype, description=desc, meta=meta, eid=eid) + etype = ybo.EntityType(name=etype, description=desc, eid=eid) eschema = schema.add_entity_type(etype) index[eid] = eschema set_perms(eschema, permsdict.get(eid, {})) @@ -167,9 +167,9 @@ seschema = schema.eschema(stype) eschema._specialized_type = stype seschema._specialized_by.append(etype) - for eid, rtype, desc, meta, sym, il in session.execute( - 'Any X,N,D,M,S,I WHERE X is CWRType, X name N, X description D, ' - 'X meta M, X symetric S, X inlined I', build_descr=False): + for eid, rtype, desc, sym, il in session.execute( + 'Any X,N,D,S,I WHERE X is CWRType, X name N, X description D, ' + 'X symetric S, X inlined I', build_descr=False): try: # bw compat: fulltext_container added in 2.47 ft_container = session.execute('Any FTC WHERE X eid %(x)s, X fulltext_container FTC', @@ -177,7 +177,7 @@ except: ft_container = None session.rollback(False) - rtype = ybo.RelationType(name=rtype, description=desc, meta=bool(meta), + rtype = ybo.RelationType(name=rtype, description=desc, symetric=bool(sym), inlined=bool(il), fulltext_container=ft_container, eid=eid) rschema = schema.add_relation_type(rtype) @@ -327,7 +327,6 @@ raise Exception("can't decode %s [was %s]" % (erschema.description, e)) return { 'name': type_, - 'meta': erschema.meta, 'final': erschema.is_final(), 'description': desc, } diff -r 986718a355fa -r 4d114865098f server/serverctl.py --- a/server/serverctl.py Thu Jul 23 12:55:51 2009 +0200 +++ b/server/serverctl.py Thu Jul 23 15:33:03 2009 +0200 @@ -73,7 +73,9 @@ """return a connection on the RDMS system table (to create/drop a user or a database """ + import logilab.common as lgp from logilab.common.adbh import get_adv_func_helper + lgp.USE_MX_DATETIME = False special_privs = '' driver = source['db-driver'] helper = get_adv_func_helper(driver) @@ -648,7 +650,7 @@ import tempfile srcappid = pop_arg(args, 1, msg="No source application specified !") destappid = pop_arg(args, msg="No destination application specified !") - output = tempfile.mktemp() + output = tempfile.mkstemp() if ':' in srcappid: host, srcappid = srcappid.split(':') _remote_dump(host, srcappid, output, self.config.sudo) diff -r 986718a355fa -r 4d114865098f server/test/data/schema.py --- a/server/test/data/schema.py Thu Jul 23 12:55:51 2009 +0200 +++ b/server/test/data/schema.py Thu Jul 23 15:33:03 2009 +0200 @@ -63,8 +63,8 @@ __specializes_schema__ = True travaille_subdivision = ObjectRelation('Personne') -_euser = import_schema('base').CWUser -_euser.__relations__[0].fulltextindexed = True +from cubicweb.schemas.base import CWUser +CWUser.get_relations('login').next().fulltextindexed = True class Note(EntityType): date = String(maxsize=10) @@ -131,14 +131,14 @@ 'delete': ('managers', RRQLExpression('O owned_by U')), } -class para(AttributeRelationType): +class para(RelationType): permissions = { 'read': ('managers', 'users', 'guests'), 'add': ('managers', ERQLExpression('X in_state S, S name "todo"')), 'delete': ('managers', ERQLExpression('X in_state S, S name "todo"')), } -class test(AttributeRelationType): +class test(RelationType): permissions = {'read': ('managers', 'users', 'guests'), 'delete': ('managers',), 'add': ('managers',)} diff -r 986718a355fa -r 4d114865098f server/test/unittest_hookhelper.py --- a/server/test/unittest_hookhelper.py Thu Jul 23 12:55:51 2009 +0200 +++ b/server/test/unittest_hookhelper.py Thu Jul 23 15:33:03 2009 +0200 @@ -41,7 +41,7 @@ from cubicweb.server import hooks, schemahooks session = self.session op1 = hooks.DelayedDeleteOp(session) - op2 = schemahooks.DelErdefOp(session) + op2 = schemahooks.DeleteRelationDefOp(session) # equivalent operation generated by op2 but replace it here by op3 so we # can check the result... op3 = schemahooks.UpdateSchemaOp(session) diff -r 986718a355fa -r 4d114865098f server/test/unittest_hooks.py --- a/server/test/unittest_hooks.py Thu Jul 23 12:55:51 2009 +0200 +++ b/server/test/unittest_hooks.py Thu Jul 23 15:33:03 2009 +0200 @@ -6,9 +6,13 @@ """ from logilab.common.testlib import TestCase, unittest_main + +from datetime import datetime + +from cubicweb import (ConnectionError, RepositoryError, ValidationError, + AuthenticationError, BadConnectionId) from cubicweb.devtools.apptest import RepositoryBasedTC, get_versions -from cubicweb import ConnectionError, RepositoryError, ValidationError, AuthenticationError, BadConnectionId from cubicweb.server.sqlutils import SQL_PREFIX from cubicweb.server.repository import Repository @@ -265,8 +269,8 @@ self.failIf(schema.has_entity('Societe2')) self.failIf(schema.has_entity('concerne2')) # schema should be update on insertion (after commit) - self.execute('INSERT CWEType X: X name "Societe2", X description "", X meta FALSE, X final FALSE') - self.execute('INSERT CWRType X: X name "concerne2", X description "", X meta FALSE, X final FALSE, X symetric FALSE') + self.execute('INSERT CWEType X: X name "Societe2", X description "", X final FALSE') + self.execute('INSERT CWRType X: X name "concerne2", X description "", X final FALSE, X symetric FALSE') self.failIf(schema.has_entity('Societe2')) self.failIf(schema.has_entity('concerne2')) self.execute('SET X read_permission G WHERE X is CWEType, X name "Societe2", G is CWGroup') @@ -614,5 +618,39 @@ self.failUnless(cu.execute("INSERT Note X: X type 'a', X in_state S WHERE S name 'todo'")) cnx.commit() + def test_metadata_cwuri(self): + eid = self.execute('INSERT Note X')[0][0] + cwuri = self.execute('Any U WHERE X eid %s, X cwuri U' % eid)[0][0] + self.assertEquals(cwuri, self.repo.config['base-url'] + 'eid/%s' % eid) + + def test_metadata_creation_modification_date(self): + _now = datetime.now() + eid = self.execute('INSERT Note X')[0][0] + creation_date, modification_date = self.execute('Any CD, MD WHERE X eid %s, ' + 'X creation_date CD, ' + 'X modification_date MD' % eid)[0] + self.assertEquals((creation_date - _now).seconds, 0) + self.assertEquals((modification_date - _now).seconds, 0) + + def test_metadata__date(self): + _now = datetime.now() + eid = self.execute('INSERT Note X')[0][0] + creation_date = self.execute('Any D WHERE X eid %s, X creation_date D' % eid)[0][0] + self.assertEquals((creation_date - _now).seconds, 0) + + def test_metadata_created_by(self): + eid = self.execute('INSERT Note X')[0][0] + self.commit() # fire operations + rset = self.execute('Any U WHERE X eid %s, X created_by U' % eid) + self.assertEquals(len(rset), 1) # make sure we have only one creator + self.assertEquals(rset[0][0], self.session.user.eid) + + def test_metadata_owned_by(self): + eid = self.execute('INSERT Note X')[0][0] + self.commit() # fire operations + rset = self.execute('Any U WHERE X eid %s, X owned_by U' % eid) + self.assertEquals(len(rset), 1) # make sure we have only one owner + self.assertEquals(rset[0][0], self.session.user.eid) + if __name__ == '__main__': unittest_main() diff -r 986718a355fa -r 4d114865098f server/test/unittest_schemaserial.py --- a/server/test/unittest_schemaserial.py Thu Jul 23 12:55:51 2009 +0200 +++ b/server/test/unittest_schemaserial.py Thu Jul 23 15:33:03 2009 +0200 @@ -23,15 +23,15 @@ def test_eschema2rql1(self): self.assertListEquals(list(eschema2rql(schema.eschema('CWAttribute'))), [ - ('INSERT CWEType X: X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s', + ('INSERT CWEType X: X description %(description)s,X final %(final)s,X name %(name)s', {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the application schema', - 'meta': True, 'name': u'CWAttribute', 'final': False}) + 'name': u'CWAttribute', 'final': False}) ]) def test_eschema2rql2(self): self.assertListEquals(list(eschema2rql(schema.eschema('String'))), [ - ('INSERT CWEType X: X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s', - {'description': u'', 'final': True, 'meta': True, 'name': u'String'})]) + ('INSERT CWEType X: X description %(description)s,X final %(final)s,X name %(name)s', + {'description': u'', 'final': True, 'name': u'String'})]) def test_eschema2rql_specialization(self): self.assertListEquals(list(specialize2rql(schema)), @@ -44,8 +44,8 @@ def test_rschema2rql1(self): self.assertListEquals(list(rschema2rql(schema.rschema('relation_type'))), [ - ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s', - {'description': u'link a relation definition to its relation type', 'meta': True, 'symetric': False, 'name': u'relation_type', 'final' : False, 'fulltext_container': None, 'inlined': True}), + ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symetric %(symetric)s', + {'description': u'link a relation definition to its relation type', 'symetric': False, 'name': u'relation_type', 'final' : False, 'fulltext_container': None, 'inlined': True}), ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s', {'rt': 'relation_type', 'description': u'', 'composite': u'object', 'oe': 'CWRType', @@ -63,7 +63,7 @@ def test_rschema2rql2(self): self.assertListEquals(list(rschema2rql(schema.rschema('add_permission'))), [ - ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s', {'description': u'core relation giving to a group the permission to add an entity or relation type', 'meta': True, 'symetric': False, 'name': u'add_permission', 'final': False, 'fulltext_container': None, 'inlined': False}), + ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symetric %(symetric)s', {'description': u'core relation giving to a group the permission to add an entity or relation type', 'symetric': False, 'name': u'add_permission', 'final': False, 'fulltext_container': None, 'inlined': False}), ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s', {'rt': 'add_permission', 'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'oe': 'CWGroup', 'ordernum': 3, 'cardinality': u'**', 'se': 'CWEType'}), @@ -79,8 +79,8 @@ def test_rschema2rql3(self): self.assertListEquals(list(rschema2rql(schema.rschema('cardinality'))), [ - ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s', - {'description': u'', 'meta': False, 'symetric': False, 'name': u'cardinality', 'final': True, 'fulltext_container': None, 'inlined': False}), + ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symetric %(symetric)s', + {'description': u'', 'symetric': False, 'name': u'cardinality', 'final': True, 'fulltext_container': None, 'inlined': False}), ('INSERT CWAttribute X: X cardinality %(cardinality)s,X defaultval %(defaultval)s,X description %(description)s,X fulltextindexed %(fulltextindexed)s,X indexed %(indexed)s,X internationalizable %(internationalizable)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s', {'rt': 'cardinality', 'description': u'subject/object cardinality', 'internationalizable': True, 'fulltextindexed': False, 'ordernum': 5, 'defaultval': None, 'indexed': False, 'cardinality': u'?1', 'oe': 'String', 'se': 'CWRelation'}), @@ -100,31 +100,31 @@ def test_updateeschema2rql1(self): self.assertListEquals(list(updateeschema2rql(schema.eschema('CWAttribute'))), - [('SET X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s WHERE X is CWEType, X name %(et)s', - {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the application schema', 'meta': True, 'et': 'CWAttribute', 'final': False, 'name': u'CWAttribute'}), + [('SET X description %(description)s,X final %(final)s,X name %(name)s WHERE X is CWEType, X name %(et)s', + {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the application schema', 'et': 'CWAttribute', 'final': False, 'name': u'CWAttribute'}), ]) def test_updateeschema2rql2(self): self.assertListEquals(list(updateeschema2rql(schema.eschema('String'))), - [('SET X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s WHERE X is CWEType, X name %(et)s', - {'description': u'', 'meta': True, 'et': 'String', 'final': True, 'name': u'String'}) + [('SET X description %(description)s,X final %(final)s,X name %(name)s WHERE X is CWEType, X name %(et)s', + {'description': u'', 'et': 'String', 'final': True, 'name': u'String'}) ]) def test_updaterschema2rql1(self): self.assertListEquals(list(updaterschema2rql(schema.rschema('relation_type'))), [ - ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s WHERE X is CWRType, X name %(rt)s', + ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symetric %(symetric)s WHERE X is CWRType, X name %(rt)s', {'rt': 'relation_type', 'symetric': False, 'description': u'link a relation definition to its relation type', - 'meta': True, 'final': False, 'fulltext_container': None, 'inlined': True, 'name': u'relation_type'}) + 'final': False, 'fulltext_container': None, 'inlined': True, 'name': u'relation_type'}) ]) def test_updaterschema2rql2(self): expected = [ - ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s WHERE X is CWRType, X name %(rt)s', + ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symetric %(symetric)s WHERE X is CWRType, X name %(rt)s', {'rt': 'add_permission', 'symetric': False, 'description': u'core relation giving to a group the permission to add an entity or relation type', - 'meta': True, 'final': False, 'fulltext_container': None, 'inlined': False, 'name': u'add_permission'}) + 'final': False, 'fulltext_container': None, 'inlined': False, 'name': u'add_permission'}) ] for i, (rql, args) in enumerate(updaterschema2rql(schema.rschema('add_permission'))): yield self.assertEquals, (rql, args), expected[i] diff -r 986718a355fa -r 4d114865098f sobjects/notification.py --- a/sobjects/notification.py Thu Jul 23 12:55:51 2009 +0200 +++ b/sobjects/notification.py Thu Jul 23 15:33:03 2009 +0200 @@ -14,7 +14,7 @@ try: from socket import gethostname except ImportError: - def gethostname(): + def gethostname(): # gae return 'XXX' from logilab.common.textutils import normalize_text @@ -76,8 +76,8 @@ return rset = entity.related('wf_info_for') try: - view = session.vreg.select_view('notif_status_change', - session, rset, row=0) + view = session.vreg.select('views', 'notif_status_change', + session, rset=rset, row=0) except RegistryException: return comment = entity.printable_value('comment', format='text/plain') @@ -100,7 +100,7 @@ rset = session.eid_rset(fromeid) vid = 'notif_%s_%s' % (self.event, rtype) try: - view = session.vreg.select_view(vid, session, rset, row=0) + view = session.vreg.select('views', vid, session, rset=rset, row=0) except RegistryException: return RenderAndSendNotificationView(session, view=view) @@ -117,7 +117,7 @@ rset = entity.as_rset() vid = 'notif_%s' % self.event try: - view = session.vreg.select_view(vid, session, rset, row=0) + view = session.vreg.select('views', vid, session, rset=rset, row=0) except RegistryException: return RenderAndSendNotificationView(session, view=view) @@ -136,7 +136,8 @@ msgid_timestamp = True def recipients(self): - finder = self.vreg.select_component('recipients_finder', self.req, self.rset) + finder = self.vreg.select('components', 'recipients_finder', self.req, + rset=self.rset) return finder.recipients() def subject(self): diff -r 986718a355fa -r 4d114865098f sobjects/supervising.py --- a/sobjects/supervising.py Thu Jul 23 12:55:51 2009 +0200 +++ b/sobjects/supervising.py Thu Jul 23 15:33:03 2009 +0200 @@ -217,8 +217,8 @@ of changes """ def _get_view(self): - return self.session.vreg.select_component('supervision_notif', - self.session, None) + return self.session.vreg.select('components', 'supervision_notif', + self.session) def _prepare_email(self): session = self.session diff -r 986718a355fa -r 4d114865098f sobjects/test/unittest_notification.py --- a/sobjects/test/unittest_notification.py Thu Jul 23 12:55:51 2009 +0200 +++ b/sobjects/test/unittest_notification.py Thu Jul 23 15:33:03 2009 +0200 @@ -56,7 +56,8 @@ self.execute('INSERT CWProperty X: X pkey "ui.language", X value "fr", X for_user U ' 'WHERE U eid %(x)s', {'x': urset[0][0]}) self.commit() # commit so that admin get its properties updated - finder = self.vreg.select_component('recipients_finder', self.request(), urset) + finder = self.vreg.select('components', 'recipients_finder', self.request(), + rset=urset) self.set_option('default-recipients-mode', 'none') self.assertEquals(finder.recipients(), []) self.set_option('default-recipients-mode', 'users') @@ -73,7 +74,7 @@ u = self.create_user('toto', req=req) assert u.req self.execute('SET X in_state S WHERE X eid %s, S name "deactivated"' % u.eid) - v = self.vreg.select_view('notif_status_change', req, u.rset, row=0) + v = self.vreg.select('views', 'notif_status_change', req, u.rset, row=0) content = v.render(row=0, comment='yeah', previous_state='activated', current_state='deactivated') diff -r 986718a355fa -r 4d114865098f spa2rql.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spa2rql.py Thu Jul 23 15:33:03 2009 +0200 @@ -0,0 +1,207 @@ +"""SPARQL -> RQL translator + +:organization: Logilab +:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses +""" +from logilab.common import make_domains +from rql import TypeResolverException +from fyzz.yappsparser import parse +from fyzz import ast + +from cubicweb.xy import xy + + +class UnsupportedQuery(Exception): pass + +def order_limit_offset(sparqlst): + addons = '' + if sparqlst.orderby: + sortterms = ', '.join('%s %s' % (var.name.upper(), ascdesc.upper()) + for var, ascdesc in sparqlst.orderby) + addons += ' ORDERBY %s' % sortterms + if sparqlst.limit: + addons += ' LIMIT %s' % sparqlst.limit + if sparqlst.offset: + addons += ' OFFSET %s' % sparqlst.offset + return addons + + +class QueryInfo(object): + """wrapper class containing necessary information to generate a RQL query + from a sparql syntax tree + """ + def __init__(self, sparqlst): + self.sparqlst = sparqlst + if sparqlst.selected == ['*']: + self.selection = [var.upper() for var in sparqlst.variables] + else: + self.selection = [var.name.upper() for var in sparqlst.selected] + self.possible_types = {} + self.infer_types_info = [] + self.union_params = [] + self.restrictions = [] + self.literals = {} + self._litcount = 0 + + def add_literal(self, value): + key = chr(ord('a') + self._litcount) + self._litcount += 1 + self.literals[key] = value + return key + + def set_possible_types(self, var, varpossibletypes): + """set/restrict possible types for the given variable. + + :return: True if something changed, else false. + :raise: TypeResolverException if no more type allowed + """ + varpossibletypes = set(varpossibletypes) + try: + ctypes = self.possible_types[var] + nbctypes = len(ctypes) + ctypes &= varpossibletypes + if not ctypes: + raise TypeResolverException() + return len(ctypes) != nbctypes + except KeyError: + self.possible_types[var] = varpossibletypes + return True + + def infer_types(self): + # XXX should use something similar to rql.analyze for proper type inference + modified = True + # loop to infer types until nothing changed + while modified: + modified = False + for yams_predicates, subjvar, obj in self.infer_types_info: + nbchoices = len(yams_predicates) + # get possible types for the subject variable, according to the + # current predicate + svptypes = set(s for s, r, o in yams_predicates) + if not '*' in svptypes: + if self.set_possible_types(subjvar, svptypes): + modified = True + # restrict predicates according to allowed subject var types + if subjvar in self.possible_types: + yams_predicates = [(s, r, o) for s, r, o in yams_predicates + if s == '*' or s in self.possible_types[subjvar]] + if isinstance(obj, ast.SparqlVar): + # make a valid rql var name + objvar = obj.name.upper() + # get possible types for the object variable, according to + # the current predicate + ovptypes = set(o for s, r, o in yams_predicates) + if not '*' in ovptypes: + if self.set_possible_types(objvar, ovptypes): + modified = True + # restrict predicates according to allowed object var types + if objvar in self.possible_types: + yams_predicates = [(s, r, o) for s, r, o in yams_predicates + if o == '*' or o in self.possible_types[objvar]] + # ensure this still make sense + if not yams_predicates: + raise TypeResolverException() + if len(yams_predicates) != nbchoices: + modified = True + + def build_restrictions(self): + # now, for each predicate + for yams_predicates, subjvar, obj in self.infer_types_info: + rel = yams_predicates[0] + # if there are several yams relation type equivalences, we will have + # to generate several unioned rql queries + for s, r, o in yams_predicates[1:]: + if r != rel[1]: + self.union_params.append((yams_predicates, subjvar, obj)) + break + # else we can simply add it to base rql restrictions + else: + restr = self.build_restriction(subjvar, rel[1], obj) + self.restrictions.append(restr) + + def build_restriction(self, subjvar, rtype, obj): + if isinstance(obj, ast.SparqlLiteral): + key = self.add_literal(obj.value) + objvar = '%%(%s)s' % key + else: + assert isinstance(obj, ast.SparqlVar) + # make a valid rql var name + objvar = obj.name.upper() + # else we can simply add it to base rql restrictions + return '%s %s %s' % (subjvar, rtype, objvar) + + def finalize(self): + """return corresponding rql query (string) / args (dict)""" + for varname, ptypes in self.possible_types.iteritems(): + if len(ptypes) == 1: + self.restrictions.append('%s is %s' % (varname, iter(ptypes).next())) + unions = [] + for releq, subjvar, obj in self.union_params: + thisunions = [] + for st, rt, ot in releq: + thisunions.append([self.build_restriction(subjvar, rt, obj)]) + if st != '*': + thisunions[-1].append('%s is %s' % (subjvar, st)) + if isinstance(obj, ast.SparqlVar) and ot != '*': + objvar = obj.name.upper() + thisunions[-1].append('%s is %s' % (objvar, objvar)) + if not unions: + unions = thisunions + else: + unions = zip(*make_domains([unions, thisunions])) + selection = 'Any ' + ', '.join(self.selection) + sparqlst = self.sparqlst + if sparqlst.distinct: + selection = 'DISTINCT ' + selection + if unions: + baserql = '%s WHERE %s' % (selection, ', '.join(self.restrictions)) + rqls = ['(%s, %s)' % (baserql, ', '.join(unionrestrs)) + for unionrestrs in unions] + rql = ' UNION '.join(rqls) + if sparqlst.orderby or sparqlst.limit or sparqlst.offset: + rql = '%s%s WITH %s BEING (%s)' % ( + selection, order_limit_offset(sparqlst), + ', '.join(self.selection), rql) + else: + rql = '%s%s WHERE %s' % (selection, order_limit_offset(sparqlst), + ', '.join(self.restrictions)) + return rql, self.literals + + +class Sparql2rqlTranslator(object): + def __init__(self, yschema): + self.yschema = yschema + + def translate(self, sparql): + sparqlst = parse(sparql) + if sparqlst.type != 'select': + raise UnsupportedQuery() + qi = QueryInfo(sparqlst) + for subj, predicate, obj in sparqlst.where: + if not isinstance(subj, ast.SparqlVar): + raise UnsupportedQuery() + # make a valid rql var name + subjvar = subj.name.upper() + if predicate == ('', 'a'): + # special 'is' relation + if not isinstance(obj, tuple): + raise UnsupportedQuery() + # restrict possible types for the subject variable + qi.set_possible_types( + subjvar, xy.yeq(':'.join(obj), isentity=True)) + else: + # 'regular' relation (eg not 'is') + if not isinstance(predicate, tuple): + raise UnsupportedQuery() + # list of 3-uple + # (yams etype (subject), yams rtype, yams etype (object)) + # where subject / object entity type may '*' if not specified + yams_predicates = xy.yeq(':'.join(predicate)) + qi.infer_types_info.append((yams_predicates, subjvar, obj)) + if not isinstance(obj, (ast.SparqlLiteral, ast.SparqlVar)): + raise UnsupportedQuery() + qi.infer_types() + qi.build_restrictions() + return qi diff -r 986718a355fa -r 4d114865098f test/unittest_cwconfig.py --- a/test/unittest_cwconfig.py Thu Jul 23 12:55:51 2009 +0200 +++ b/test/unittest_cwconfig.py Thu Jul 23 15:33:03 2009 +0200 @@ -8,7 +8,6 @@ import sys import os from os.path import dirname, join, abspath -from tempfile import mktemp from logilab.common.testlib import TestCase, unittest_main from logilab.common.changelog import Version diff -r 986718a355fa -r 4d114865098f test/unittest_cwctl.py --- a/test/unittest_cwctl.py Thu Jul 23 12:55:51 2009 +0200 +++ b/test/unittest_cwctl.py Thu Jul 23 15:33:03 2009 +0200 @@ -13,9 +13,9 @@ if os.environ.get('APYCOT_ROOT'): root = os.environ['APYCOT_ROOT'] CUBES_DIR = '%s/local/share/cubicweb/cubes/' % root - os.environ['CW_CUBES'] = CUBES_DIR + os.environ['CW_CUBES_PATH'] = CUBES_DIR REGISTRY_DIR = '%s/etc/cubicweb.d/' % root - os.environ['CW_REGISTRY_DIR'] = REGISTRY_DIR + os.environ['CW_INSTANCES_DIR'] = REGISTRY_DIR from cubicweb.cwconfig import CubicWebConfiguration CubicWebConfiguration.load_cwctl_plugins() diff -r 986718a355fa -r 4d114865098f test/unittest_schema.py --- a/test/unittest_schema.py Thu Jul 23 12:55:51 2009 +0200 +++ b/test/unittest_schema.py Thu Jul 23 15:33:03 2009 +0200 @@ -150,7 +150,7 @@ 'CWCache', 'CWConstraint', 'CWConstraintType', 'CWEType', 'CWAttribute', 'CWGroup', 'EmailAddress', 'CWRelation', 'CWPermission', 'CWProperty', 'CWRType', 'CWUser', - 'File', 'Float', 'Image', 'Int', 'Interval', 'Note', + 'ExternalUri', 'File', 'Float', 'Image', 'Int', 'Interval', 'Note', 'Password', 'Personne', 'RQLExpression', 'Societe', 'State', 'String', 'SubNote', 'Tag', 'Time', @@ -163,7 +163,7 @@ 'cardinality', 'comment', 'comment_format', 'composite', 'condition', 'connait', 'constrained_by', 'content', - 'content_format', 'created_by', 'creation_date', 'cstrtype', + 'content_format', 'created_by', 'creation_date', 'cstrtype', 'cwuri', 'data', 'data_encoding', 'data_format', 'defaultval', 'delete_permission', 'description', 'description_format', 'destination_state', @@ -179,7 +179,7 @@ 'label', 'last_login_time', 'login', - 'mainvars', 'meta', 'modification_date', + 'mainvars', 'modification_date', 'name', 'nom', @@ -193,7 +193,7 @@ 'tags', 'timestamp', 'title', 'to_entity', 'to_state', 'transition_of', 'travaille', 'type', - 'upassword', 'update_permission', 'use_email', + 'upassword', 'update_permission', 'uri', 'use_email', 'value', @@ -203,7 +203,7 @@ eschema = schema.eschema('CWUser') rels = sorted(str(r) for r in eschema.subject_relations()) - self.assertListEquals(rels, ['created_by', 'creation_date', 'eid', + self.assertListEquals(rels, ['created_by', 'creation_date', 'cwuri', 'eid', 'evaluee', 'firstname', 'has_text', 'identity', 'in_group', 'in_state', 'is', 'is_instance_of', 'last_login_time', diff -r 986718a355fa -r 4d114865098f test/unittest_spa2rql.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/unittest_spa2rql.py Thu Jul 23 15:33:03 2009 +0200 @@ -0,0 +1,180 @@ +from logilab.common.testlib import TestCase, unittest_main +from cubicweb.devtools import TestServerConfiguration +from cubicweb.xy import xy +from cubicweb.spa2rql import Sparql2rqlTranslator + +xy.add_equivalence('Project', 'doap:Project') +xy.add_equivalence('Project creation_date', 'doap:Project doap:created') +xy.add_equivalence('Project name', 'doap:Project doap:name') + + +config = TestServerConfiguration('data') +config.bootstrap_cubes() +schema = config.load_schema() + + +class XYTC(TestCase): + def setUp(self): + self.tr = Sparql2rqlTranslator(schema) + + def _test(self, sparql, rql, args={}): + qi = self.tr.translate(sparql) + self.assertEquals(qi.finalize(), (rql, args)) + + def XXX_test_base_01(self): + self._test('SELECT * WHERE { }', 'Any X') + + + def test_base_is(self): + self._test(''' + PREFIX doap: + SELECT ?project + WHERE { + ?project a doap:Project; + }''', 'Any PROJECT WHERE PROJECT is Project') + + + def test_base_attr_sel(self): + self._test(''' + PREFIX doap: + SELECT ?created + WHERE { + ?project a doap:Project; + doap:created ?created. + }''', 'Any CREATED WHERE PROJECT creation_date CREATED, PROJECT is Project') + + + def test_base_attr_sel_distinct(self): + self._test(''' + PREFIX doap: + SELECT DISTINCT ?name + WHERE { + ?project a doap:Project; + doap:name ?name. + }''', 'DISTINCT Any NAME WHERE PROJECT name NAME, PROJECT is Project') + + + def test_base_attr_sel_reduced(self): + self._test(''' + PREFIX doap: + SELECT REDUCED ?name + WHERE { + ?project a doap:Project; + doap:name ?name. + }''', 'Any NAME WHERE PROJECT name NAME, PROJECT is Project') + + + def test_base_attr_sel_limit_offset(self): + self._test(''' + PREFIX doap: + SELECT ?name + WHERE { + ?project a doap:Project; + doap:name ?name. + } + LIMIT 20''', 'Any NAME LIMIT 20 WHERE PROJECT name NAME, PROJECT is Project') + self._test(''' + PREFIX doap: + SELECT ?name + WHERE { + ?project a doap:Project; + doap:name ?name. + } + LIMIT 20 OFFSET 10''', 'Any NAME LIMIT 20 OFFSET 10 WHERE PROJECT name NAME, PROJECT is Project') + + + def test_base_attr_sel_orderby(self): + self._test(''' + PREFIX doap: + SELECT ?name + WHERE { + ?project a doap:Project; + doap:name ?name; + doap:created ?created. + } + ORDER BY ?name DESC(?created)''', 'Any NAME ORDERBY NAME ASC, CREATED DESC WHERE PROJECT name NAME, PROJECT creation_date CREATED, PROJECT is Project') + + + def test_base_any_attr_sel(self): + self._test(''' + PREFIX dc: + SELECT ?x ?cd + WHERE { + ?x dc:date ?cd; + }''', 'Any X, CD WHERE X creation_date CD') + + + def test_base_any_attr_sel_amb(self): + xy.add_equivalence('Version publication_date', 'doap:Version dc:date') + try: + self._test(''' + PREFIX dc: + SELECT ?x ?cd + WHERE { + ?x dc:date ?cd; + }''', '(Any X, CD WHERE , X creation_date CD) UNION (Any X, CD WHERE , X publication_date CD, X is Version)') + finally: + xy.remove_equivalence('Version publication_date', 'doap:Version dc:date') + + + def test_base_any_attr_sel_amb_limit_offset(self): + xy.add_equivalence('Version publication_date', 'doap:Version dc:date') + try: + self._test(''' + PREFIX dc: + SELECT ?x ?cd + WHERE { + ?x dc:date ?cd; + } + LIMIT 20 OFFSET 10''', 'Any X, CD LIMIT 20 OFFSET 10 WITH X, CD BEING ((Any X, CD WHERE , X creation_date CD) UNION (Any X, CD WHERE , X publication_date CD, X is Version))') + finally: + xy.remove_equivalence('Version publication_date', 'doap:Version dc:date') + + + def test_base_any_attr_sel_amb_orderby(self): + xy.add_equivalence('Version publication_date', 'doap:Version dc:date') + try: + self._test(''' + PREFIX dc: + SELECT ?x ?cd + WHERE { + ?x dc:date ?cd; + } + ORDER BY DESC(?cd)''', 'Any X, CD ORDERBY CD DESC WITH X, CD BEING ((Any X, CD WHERE , X creation_date CD) UNION (Any X, CD WHERE , X publication_date CD, X is Version))') + finally: + xy.remove_equivalence('Version publication_date', 'doap:Version dc:date') + + + def test_restr_attr(self): + self._test(''' + PREFIX doap: + SELECT ?project + WHERE { + ?project a doap:Project; + doap:name "cubicweb". + }''', 'Any PROJECT WHERE PROJECT name %(a)s, PROJECT is Project', {'a': 'cubicweb'}) + +# # Two elements in the group +# PREFIX : +# SELECT * +# { :p :q :r OPTIONAL { :a :b :c } +# :p :q :r OPTIONAL { :a :b :c } +# } + +# PREFIX : +# SELECT * +# { +# { ?s ?p ?o } UNION { ?a ?b ?c } +# } + +# PREFIX dob: +# PREFIX time: +# PREFIX dc: +# SELECT ?desc +# WHERE { +# dob:1D a time:ProperInterval; +# dc:description ?desc. +# } + +if __name__ == '__main__': + unittest_main() diff -r 986718a355fa -r 4d114865098f view.py --- a/view.py Thu Jul 23 12:55:51 2009 +0200 +++ b/view.py Thu Jul 23 15:33:03 2009 +0200 @@ -334,7 +334,7 @@ default_rql = None - def __init__(self, req, rset, **kwargs): + def __init__(self, req, rset=None, **kwargs): super(EntityStartupView, self).__init__(req, rset, **kwargs) if rset is None: # this instance is not in the "entityview" category diff -r 986718a355fa -r 4d114865098f vregistry.py --- a/vregistry.py Thu Jul 23 12:55:51 2009 +0200 +++ b/vregistry.py Thu Jul 23 15:33:03 2009 +0200 @@ -22,10 +22,11 @@ __docformat__ = "restructuredtext en" import sys +import types from os import listdir, stat from os.path import dirname, join, realpath, split, isdir, exists from logging import getLogger -import types +from warnings import warn from cubicweb import CW_SOFTWARE_ROOT, set_log_methods from cubicweb import RegistryNotFound, ObjectNotFound, NoSelectableObject @@ -169,25 +170,94 @@ If no oid is given, return all objects in this registry """ registry = self.registry(name) - if oid: + if oid is not None: try: return registry[oid] except KeyError: raise ObjectNotFound(oid), None, sys.exc_info()[-1] - else: - result = [] - for objs in registry.values(): - result += objs - return result + result = [] + for objs in registry.values(): + result += objs + return result + + # dynamic selection methods ################################################ - def object_by_id(self, registry, cid, *args, **kwargs): - """return the most specific component according to the resultset""" - objects = self[registry][cid] + def object_by_id(self, registry, oid, *args, **kwargs): + """return object in . + + raise `ObjectNotFound` if not object with id in + raise `AssertionError` if there is more than one object there + """ + objects = self.registry_objects(registry, oid) assert len(objects) == 1, objects return objects[0].selected(*args, **kwargs) + def select(self, registry, oid, *args, **kwargs): + """return the most specific object in . according to + the given context + + raise `ObjectNotFound` if not object with id in + raise `NoSelectableObject` if not object apply + """ + return self.select_best(self.registry_objects(registry, oid), + *args, **kwargs) + + def select_object(self, registry, oid, *args, **kwargs): + """return the most specific object in . according to + the given context, or None if no object apply + """ + try: + return self.select(registry, oid, *args, **kwargs) + except (NoSelectableObject, ObjectNotFound): + return None + + def possible_objects(self, registry, *args, **kwargs): + """return an iterator on possible objects in for the given + context + """ + for vobjects in self.registry(registry).itervalues(): + try: + yield self.select_best(vobjects, *args, **kwargs) + except NoSelectableObject: + continue + + def select_best(self, vobjects, *args, **kwargs): + """return an instance of the most specific object according + to parameters + + raise `NoSelectableObject` if not object apply + """ + if len(args) > 1: + warn('only the request param can not be named when calling select', + DeprecationWarning, stacklevel=3) + score, winners = 0, [] + for vobject in vobjects: + vobjectscore = vobject.__select__(vobject, *args, **kwargs) + if vobjectscore > score: + score, winners = vobjectscore, [vobject] + elif vobjectscore > 0 and vobjectscore == score: + winners.append(vobject) + if not winners: + raise NoSelectableObject('args: %s\nkwargs: %s %s' + % (args, kwargs.keys(), + [repr(v) for v in vobjects])) + if len(winners) > 1: + if self.config.mode == 'installed': + self.error('select ambiguity, args: %s\nkwargs: %s %s', + args, kwargs.keys(), [repr(v) for v in winners]) + else: + raise Exception('select ambiguity, args: %s\nkwargs: %s %s' + % (args, kwargs.keys(), + [repr(v) for v in winners])) + # return the result of the .selected method of the vobject + return winners[0].selected(*args, **kwargs) + # methods for explicit (un)registration ################################### +# def clear(self, key): +# regname, oid = key.split('.') +# self[regname].pop(oid, None) + def register_all(self, objects, modname, butclasses=()): for obj in objects: try: @@ -259,52 +329,6 @@ replaced, obj) self.register(obj, registryname=registryname) - # dynamic selection methods ############################################### - - def select(self, vobjects, *args, **kwargs): - """return an instance of the most specific object according - to parameters - - raise NoSelectableObject if not object apply - """ - score, winners = 0, [] - for vobject in vobjects: - vobjectscore = vobject.__select__(vobject, *args, **kwargs) - if vobjectscore > score: - score, winners = vobjectscore, [vobject] - elif vobjectscore > 0 and vobjectscore == score: - winners.append(vobject) - if not winners: - raise NoSelectableObject('args: %s\nkwargs: %s %s' - % (args, kwargs.keys(), - [repr(v) for v in vobjects])) - if len(winners) > 1: - if self.config.mode == 'installed': - self.error('select ambiguity, args: %s\nkwargs: %s %s', - args, kwargs.keys(), [repr(v) for v in winners]) - else: - raise Exception('select ambiguity, args: %s\nkwargs: %s %s' - % (args, kwargs.keys(), - [repr(v) for v in winners])) - winner = winners[0] - # return the result of the .selected method of the vobject - return winner.selected(*args, **kwargs) - - def possible_objects(self, registry, *args, **kwargs): - """return an iterator on possible objects in a registry for this result set - - actions returned are classes, not instances - """ - for vobjects in self.registry(registry).values(): - try: - yield self.select(vobjects, *args, **kwargs) - except NoSelectableObject: - continue - - def select_object(self, registry, cid, *args, **kwargs): - """return the most specific component according to the resultset""" - return self.select(self.registry_objects(registry, cid), *args, **kwargs) - # intialization methods ################################################### def init_registration(self, path, extrapath=None): diff -r 986718a355fa -r 4d114865098f web/application.py --- a/web/application.py Thu Jul 23 12:55:51 2009 +0200 +++ b/web/application.py Thu Jul 23 15:33:03 2009 +0200 @@ -10,22 +10,24 @@ import sys from time import clock, time +from logilab.common.deprecation import obsolete + from rql import BadRQLQuery -from cubicweb import set_log_methods -from cubicweb import (ValidationError, Unauthorized, AuthenticationError, - NoSelectableObject, RepositoryError) -from cubicweb.cwvreg import CubicWebRegistry -from cubicweb.web import (LOGGER, StatusResponse, DirectResponse, Redirect, - NotFound, RemoteCallFailed, ExplicitLogin, - InvalidSession, RequestError) -from cubicweb.web.component import Component +from cubicweb import set_log_methods, cwvreg +from cubicweb import ( + ValidationError, Unauthorized, AuthenticationError, NoSelectableObject, + RepositoryError) +from cubicweb.web import LOGGER, component +from cubicweb.web import ( + StatusResponse, DirectResponse, Redirect, NotFound, + RemoteCallFailed, ExplicitLogin, InvalidSession, RequestError) # make session manager available through a global variable so the debug view can # print information about web session SESSION_MANAGER = None -class AbstractSessionManager(Component): +class AbstractSessionManager(component.Component): """manage session data associated to a session identifier""" id = 'sessionmanager' @@ -39,8 +41,7 @@ if self.session_time: assert self.cleanup_session_time < self.session_time assert self.cleanup_anon_session_time < self.session_time - self.authmanager = self.vreg.select_component('authmanager') - assert self.authmanager, 'no authentication manager found' + self.authmanager = self.vreg.select('components', 'authmanager') def clean_sessions(self): """cleanup sessions which has not been unused since a given amount of @@ -88,7 +89,7 @@ raise NotImplementedError() -class AbstractAuthenticationManager(Component): +class AbstractAuthenticationManager(component.Component): """authenticate user associated to a request and check session validity""" id = 'authmanager' @@ -111,8 +112,7 @@ SESSION_VAR = '__session' def __init__(self, appli): - self.session_manager = appli.vreg.select_component('sessionmanager') - assert self.session_manager, 'no session manager found' + self.session_manager = appli.vreg.select('components', 'sessionmanager') global SESSION_MANAGER SESSION_MANAGER = self.session_manager if not 'last_login_time' in appli.vreg.schema: @@ -210,20 +210,8 @@ class CubicWebPublisher(object): - """Central registry for the web application. This is one of the central - object in the web application, coupling dynamically loaded objects with - the application's schema and the application's configuration objects. - - It specializes the VRegistry by adding some convenience methods to - access to stored objects. Currently we have the following registries - of objects known by the web application (library may use some others - additional registries): - * controllers, which are directly plugged into the application - object to handle request publishing - * views - * templates - * components - * actions + """the publisher is a singleton hold by the web frontend, and is responsible + to publish HTTP request. """ def __init__(self, config, debug=None, @@ -232,7 +220,7 @@ super(CubicWebPublisher, self).__init__() # connect to the repository and get application's schema if vreg is None: - vreg = CubicWebRegistry(config, debug=debug) + vreg = cwvreg.CubicWebRegistry(config, debug=debug) self.vreg = vreg self.info('starting web application from %s', config.apphome) self.repo = config.repository(vreg) @@ -251,7 +239,7 @@ self.publish = self.main_publish # instantiate session and url resolving helpers self.session_handler = session_handler_fact(self) - self.url_resolver = vreg.select_component('urlpublisher') + self.url_resolver = vreg.select('components', 'urlpublisher') def connect(self, req): """return a connection for a logged user object according to existing @@ -260,15 +248,6 @@ """ self.session_handler.set_session(req) - def select_controller(self, oid, req): - """return the most specific view according to the resultset""" - vreg = self.vreg - try: - return vreg.select(vreg.registry_objects('controllers', oid), - req=req, appli=self) - except NoSelectableObject: - raise Unauthorized(req._('not authorized')) - # publish methods ######################################################### def log_publish(self, path, req): @@ -293,6 +272,13 @@ finally: self._logfile_lock.release() + @obsolete("use vreg.select('controllers', ...)") + def select_controller(self, oid, req): + try: + return self.vreg.select('controllers', oid, req=req, appli=self) + except NoSelectableObject: + raise Unauthorized(req._('not authorized')) + def main_publish(self, path, req): """method called by the main publisher to process @@ -317,7 +303,11 @@ try: try: ctrlid, rset = self.url_resolver.process(req, path) - controller = self.select_controller(ctrlid, req) + try: + controller = self.vreg.select('controllers', ctrlid, req, + appli=self) + except NoSelectableObject: + raise Unauthorized(req._('not authorized')) req.update_search_state() result = controller.publish(rset=rset) if req.cnx is not None: @@ -385,7 +375,7 @@ if tb: req.data['excinfo'] = excinfo req.form['vid'] = 'error' - errview = self.vreg.select_view('error', req, None) + errview = self.vreg.select('views', 'error', req) template = self.main_template_id(req) content = self.vreg.main_template(req, template, view=errview) except: @@ -400,7 +390,7 @@ def notfound_content(self, req): req.form['vid'] = '404' - view = self.vreg.select_view('404', req, None) + view = self.vreg.select('views', '404', req) template = self.main_template_id(req) return self.vreg.main_template(req, template, view=view) diff -r 986718a355fa -r 4d114865098f web/box.py --- a/web/box.py Thu Jul 23 12:55:51 2009 +0200 +++ b/web/box.py Thu Jul 23 15:33:03 2009 +0200 @@ -219,8 +219,8 @@ return entity.unrelated(self.rtype, self.etype, get_role(self)).entities() # in other cases, use vocabulary functions entities = [] - form = self.vreg.select_object('forms', 'edition', self.req, self.rset, - row=self.row or 0) + form = self.vreg.select('forms', 'edition', self.req, rset=self.rset, + row=self.row or 0) field = form.field_by_name(self.rtype, get_role(self), entity.e_schema) for _, eid in form.form_field_vocabulary(field): if eid is not None: diff -r 986718a355fa -r 4d114865098f web/component.py diff -r 986718a355fa -r 4d114865098f web/controller.py --- a/web/controller.py Thu Jul 23 12:55:51 2009 +0200 +++ b/web/controller.py Thu Jul 23 15:33:03 2009 +0200 @@ -86,14 +86,16 @@ def process_rql(self, rql): """execute rql if specified""" + # XXX assigning to self really necessary? + self.rset = None if rql: self.ensure_ro_rql(rql) if not isinstance(rql, unicode): rql = unicode(rql, self.req.encoding) - pp = self.vreg.select_component('magicsearch', self.req) - self.rset = pp.process_query(rql, self.req) - return self.rset - return None + pp = self.vreg.select_object('components', 'magicsearch', self.req) + if pp is not None: + self.rset = pp.process_query(rql, self.req) + return self.rset def check_expected_params(self, params): """check that the given list of parameters are specified in the form diff -r 986718a355fa -r 4d114865098f web/data/cubicweb.edition.js diff -r 986718a355fa -r 4d114865098f web/data/cubicweb.htmlhelpers.js --- a/web/data/cubicweb.htmlhelpers.js Thu Jul 23 12:55:51 2009 +0200 +++ b/web/data/cubicweb.htmlhelpers.js Thu Jul 23 15:33:03 2009 +0200 @@ -16,78 +16,6 @@ return ''; } -// XXX this is used exactly ONCE in web/views/massmailing.py -function insertText(text, areaId) { - var textarea = jQuery('#' + areaId); - if (document.selection) { // IE - var selLength; - textarea.focus(); - sel = document.selection.createRange(); - selLength = sel.text.length; - sel.text = text; - sel.moveStart('character', selLength-text.length); - sel.select(); - } else if (textarea.selectionStart || textarea.selectionStart == '0') { // mozilla - var startPos = textarea.selectionStart; - var endPos = textarea.selectionEnd; - // insert text so that it replaces the [startPos, endPos] part - textarea.value = textarea.value.substring(0,startPos) + text + textarea.value.substring(endPos,textarea.value.length); - // set cursor pos at the end of the inserted text - textarea.selectionStart = textarea.selectionEnd = startPos+text.length; - textarea.focus(); - } else { // safety belt for other browsers - textarea.value += text; - } -} - -/* taken from dojo toolkit */ -// XXX this looks unused -function setCaretPos(element, start, end){ - if(!end){ end = element.value.length; } // NOTE: Strange - should be able to put caret at start of text? - // Mozilla - // parts borrowed from http://www.faqts.com/knowledge_base/view.phtml/aid/13562/fid/130 - if(element.setSelectionRange){ - element.focus(); - element.setSelectionRange(start, end); - } else if(element.createTextRange){ // IE - var range = element.createTextRange(); - with(range){ - collapse(true); - moveEnd('character', end); - moveStart('character', start); - select(); - } - } else { //otherwise try the event-creation hack (our own invention) - // do we need these? - element.value = element.value; - element.blur(); - element.focus(); - // figure out how far back to go - var dist = parseInt(element.value.length)-end; - var tchar = String.fromCharCode(37); - var tcc = tchar.charCodeAt(0); - for(var x = 0; x < dist; x++){ - var te = document.createEvent("KeyEvents"); - te.initKeyEvent("keypress", true, true, null, false, false, false, false, tcc, tcc); - element.dispatchEvent(te); - } - } -} - - -// XXX this looks unused -function setProgressMessage(label) { - var body = document.getElementsByTagName('body')[0]; - body.appendChild(DIV({id: 'progress'}, label)); - jQuery('#progress').show(); -} - -// XXX this looks unused -function resetProgressMessage() { - var body = document.getElementsByTagName('body')[0]; - jQuery('#progress').hide(); -} - /* set body's cursor to 'progress' */ function setProgressCursor() { @@ -151,7 +79,7 @@ /* toggles visibility of login popup div */ -// XXX used exactly ONCE +// XXX used exactly ONCE in basecomponents function popupLoginBox() { toggleVisibility('popupLoginBox'); jQuery('#__login:visible').focus(); @@ -193,14 +121,6 @@ forEach(filter(filterfunc, elements), function(cb) {cb.checked=checked;}); } -/* centers an HTML element on the screen */ -// XXX looks unused -function centerElement(obj){ - var vpDim = getViewportDimensions(); - var elemDim = getElementDimensions(obj); - setElementPosition(obj, {'x':((vpDim.w - elemDim.w)/2), - 'y':((vpDim.h - elemDim.h)/2)}); -} /* this function is a hack to build a dom node from html source */ function html2dom(source) { @@ -223,12 +143,6 @@ function isTextNode(domNode) { return domNode.nodeType == 3; } function isElementNode(domNode) { return domNode.nodeType == 1; } -// XXX this looks unused -function changeLinkText(link, newText) { - jQuery(link).text(newText); -} - - function autogrow(area) { if (area.scrollHeight > area.clientHeight && !window.opera) { if (area.rows < 20) { @@ -236,13 +150,6 @@ } } } - -// XXX this looks unused -function limitTextAreaSize(textarea, size) { - var $area = jQuery(textarea); - $area.val($area.val().slice(0, size)); -} - //============= page loading events ==========================================// CubicWeb.rounded = [ @@ -250,8 +157,6 @@ ['div.boxTitle, div.boxPrefTitle, div.sideBoxTitle, th.month', 'top 6px'] ]; - - function roundedCorners(node) { node = jQuery(node); for(var r=0; r < CubicWeb.rounded.length; r++) { @@ -259,7 +164,8 @@ } } -jQuery(document).ready(function () {roundedCorners(this.body)}); +jQuery(document).ready(function () {roundedCorners(this.body);}); + +CubicWeb.provide('corners.js'); CubicWeb.provide('htmlhelpers.js'); - diff -r 986718a355fa -r 4d114865098f web/data/cubicweb.massmailing.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/data/cubicweb.massmailing.js Thu Jul 23 15:33:03 2009 +0200 @@ -0,0 +1,23 @@ + +function insertText(text, areaId) { + var textarea = jQuery('#' + areaId); + if (document.selection) { // IE + var selLength; + textarea.focus(); + sel = document.selection.createRange(); + selLength = sel.text.length; + sel.text = text; + sel.moveStart('character', selLength-text.length); + sel.select(); + } else if (textarea.selectionStart || textarea.selectionStart == '0') { // mozilla + var startPos = textarea.selectionStart; + var endPos = textarea.selectionEnd; + // insert text so that it replaces the [startPos, endPos] part + textarea.value = textarea.value.substring(0,startPos) + text + textarea.value.substring(endPos,textarea.value.length); + // set cursor pos at the end of the inserted text + textarea.selectionStart = textarea.selectionEnd = startPos+text.length; + textarea.focus(); + } else { // safety belt for other browsers + textarea.value += text; + } +} diff -r 986718a355fa -r 4d114865098f web/data/cubicweb.python.js --- a/web/data/cubicweb.python.js Thu Jul 23 12:55:51 2009 +0200 +++ b/web/data/cubicweb.python.js Thu Jul 23 15:33:03 2009 +0200 @@ -157,7 +157,7 @@ // ========== ARRAY EXTENSIONS ========== /// Array.prototype.contains = function(element) { return findValue(this, element) != -1; -} +}; // ========== END OF ARRAY EXTENSIONS ========== /// @@ -201,7 +201,7 @@ * [0,2,4,6,8] */ function list(iterable) { - iterator = iter(iterable); + var iterator = iter(iterable); var result = []; while (true) { /* iterates until StopIteration occurs */ @@ -267,14 +267,6 @@ function min() { return listMin(arguments); } function max() { return listMax(arguments); } -// tricky multiple assign -// function assign(lst, varnames) { -// var length = min(lst.length, varnames.length); -// for(var i=0; i>> d = dict(["x", "y", "z"], [0, 1, 2]) * >>> d['y'] @@ -335,7 +327,7 @@ function makeConstructor(userctor) { return function() { // this is a proxy to user's __init__ - if(userctor) { + if (userctor) { userctor.apply(this, arguments); } }; @@ -369,7 +361,7 @@ } } var userctor = basemeths['__init__']; - constructor = makeConstructor(userctor); + var constructor = makeConstructor(userctor); // python-like interface constructor.__name__ = name; diff -r 986718a355fa -r 4d114865098f web/data/cubicweb.widgets.js --- a/web/data/cubicweb.widgets.js Thu Jul 23 12:55:51 2009 +0200 +++ b/web/data/cubicweb.widgets.js Thu Jul 23 15:33:03 2009 +0200 @@ -184,8 +184,7 @@ Widgets.TreeView = defclass("TreeView", null, { __init__: function(wdgnode) { jQuery(wdgnode).treeview({toggle: toggleTree, - prerendered: true - }); + prerendered: true}); } }); diff -r 986718a355fa -r 4d114865098f web/data/external_resources --- a/web/data/external_resources Thu Jul 23 12:55:51 2009 +0200 +++ b/web/data/external_resources Thu Jul 23 15:33:03 2009 +0200 @@ -18,7 +18,7 @@ #IE_STYLESHEETS = DATADIR/cubicweb.ie.css # Javascripts files to include in HTML headers -#JAVASCRIPTS = DATADIR/jqyery.js, DATADIR/cubicweb.python.js, DATADIR/jquery.json.js, DATADIR/cubicweb.compat.js, DATADIR/cubicweb.htmlhelpers.js +#JAVASCRIPTS = DATADIR/jquery.js, DATADIR/cubicweb.python.js, DATADIR/jquery.json.js, DATADIR/cubicweb.compat.js, DATADIR/cubicweb.htmlhelpers.js # path to favicon (relative to the application main script, seen as a # directory, hence .. when you are not using an absolute path) diff -r 986718a355fa -r 4d114865098f web/data/jquery.tools.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/data/jquery.tools.min.js Thu Jul 23 15:33:03 2009 +0200 @@ -0,0 +1,20 @@ +/* + * jquery.tools 1.0.2 - The missing UI library + * + * [tools.tabs-1.0.1, tools.tooltip-1.0.2, tools.scrollable-1.0.5, tools.overlay-1.0.4, tools.expose-1.0.3] + * + * Copyright (c) 2009 Tero Piirainen + * http://flowplayer.org/tools/ + * + * Dual licensed under MIT and GPL 2+ licenses + * http://www.opensource.org/licenses + * + * ----- + * + * Build: Fri Jun 12 12:37:07 GMT+00:00 2009 + */ +(function(c){c.tools=c.tools||{version:{}};c.tools.version.tabs="1.0.1";c.tools.addTabEffect=function(d,e){b[d]=e};var b={"default":function(d){this.getPanes().hide().eq(d).show()},fade:function(d){this.getPanes().hide().eq(d).fadeIn(this.getConf().fadeInSpeed)},slide:function(d){this.getCurrentPane().slideUp("fast");this.getPanes().eq(d).slideDown()},horizontal:function(d){if(!c._hW){c._hW=this.getPanes().eq(0).width()}this.getCurrentPane().animate({width:0},function(){c(this).hide()});this.getPanes().eq(d).animate({width:c._hW},function(){c(this).show()})}};function a(e,f,g){var d=this;var h;function i(j,k){c(d).bind(j,function(m,l){if(k&&k.call(this,l.index)===false&&l){l.proceed=false}});return d}c.each(g,function(j,k){if(c.isFunction(k)){i(j,k)}});c.extend(this,{click:function(k){if(k===h){return d}var m=d.getCurrentPane();var l=e.eq(k);if(typeof k=="string"){l=e.filter("[href="+k+"]");k=e.index(l)}if(!l.length){if(h>=0){return d}k=g.initialIndex;l=e.eq(k)}var j={index:k,proceed:true};c(d).triggerHandler("onBeforeClick",j);if(!j.proceed){return d}l.addClass(g.current);b[g.effect].call(d,k);c(d).triggerHandler("onClick",j);e.removeClass(g.current);l.addClass(g.current);h=k;return d},getConf:function(){return g},getTabs:function(){return e},getPanes:function(){return f},getCurrentPane:function(){return f.eq(h)},getCurrentTab:function(){return e.eq(h)},getIndex:function(){return h},next:function(){return d.click(h+1)},prev:function(){return d.click(h-1)},onBeforeClick:function(j){return i("onBeforeClick",j)},onClick:function(j){return i("onClick",j)}});e.each(function(j){c(this).bind(g.event,function(k){d.click(j);if(!g.history){return k.preventDefault()}})});if(g.history){e.history(function(j,k){d.click(k||0)})}if(location.hash){d.click(location.hash)}else{d.click(g.initialIndex)}f.find("a[href^=#]").click(function(){d.click(c(this).attr("href"))})}c.fn.tabs=function(g,d){var e=this.eq(typeof conf=="number"?conf:0).data("tabs");if(e){return e}var f={tabs:"a",current:"current",onBeforeClick:null,onClick:null,effect:"default",history:false,initialIndex:0,event:"click",api:false};if(c.isFunction(d)){d={onBeforeClick:d}}c.extend(f,d);this.each(function(){var h=c(this).find(f.tabs);if(!h.length){h=c(this).children()}var i=g.jquery?g:c(g);e=new a(h,i,f);c(this).data("tabs",e)});return f.api?e:this}})(jQuery);(function(b){var c,a;b.prototype.history=function(e){var d=this;if(b.browser.msie){if(!a){a=b("