# HG changeset patch # User Adrien Di Mascio # Date 1249391169 -7200 # Node ID ff6114c2c416b7e169b69ef17f1ec2bcef448f5a # Parent c66f52d443943ae0fbc565a9a1d99cd4a258793e# Parent 9e639925ca2fecc37867fa93ce68838e2b0a95bb merge (after jquery.tools backout) diff -r 9e639925ca2f -r ff6114c2c416 .hgtags --- a/.hgtags Tue Aug 04 11:43:03 2009 +0200 +++ b/.hgtags Tue Aug 04 15:06:09 2009 +0200 @@ -40,3 +40,13 @@ 4003d24974f15f17bd03b7efd6a5047cad4e4c41 cubicweb-debian-version-3_2_3-1 2d7d3062ca03d4b4144100013dc4ab7f9d9cb25e cubicweb-version-3_3_0 07214e923e75c8f0490e609e9bee0f4964b87114 cubicweb-debian-version-3_3_0-1 +a356da3e725bfcb59d8b48a89d04be05ea261fd3 3.3.1 +e3aeb6e6c3bb5c18e8dcf61bae9d654beda6c036 cubicweb-version-3_3_2 +bef5e74e53f9de8220451dca4b5863a24a0216fb cubicweb-debian-version-3_3_2-1 +1cf9e44e2f1f4415253b8892a0adfbd3b69e84fd cubicweb-version-3_3_3 +81973c897c9e78e5e52643e03628654916473196 cubicweb-debian-version-3_3_3-1 +1cf9e44e2f1f4415253b8892a0adfbd3b69e84fd cubicweb-version-3_3_3 +47b5236774a0cf3b1cfe75f6d4bd2ec989644ace cubicweb-version-3_3_3 +2ba27ce8ecd9828693ec53c517e1c8810cbbe33e cubicweb-debian-version-3_3_3-2 +d46363eac5d71bc1570d69337955154dfcd8fcc8 cubicweb-version-3.3.4 +7dc22caa7640bf70fcae55afb6d2326829dacced cubicweb-debian-version-3.3.4-1 diff -r 9e639925ca2f -r ff6114c2c416 README --- a/README Tue Aug 04 11:43:03 2009 +0200 +++ b/README Tue Aug 04 15:06:09 2009 +0200 @@ -1,18 +1,27 @@ -CubicWeb semantic web framework +CubicWeb semantic web framework =============================== - + Install ------- -From the source distribution, extract the tarball and run :: - - python setup.py install - -For deb and rpm packages, use the tools recommended by your distribution. + +More details at http://www.cubicweb.org/doc/en/admin/setup + +Getting started +--------------- + +Execute: - + apt-get install cubicweb cubicweb-dev cubicweb-blog + cubicweb-ctl create blog myblog + cubicweb-ctl start -D myblog + sensible-browser http://localhost:8080/ + +Details at http://www.cubicweb.org/doc/en/intro/tutorial/blog-in-five-minutes + Documentation ------------- -Look in the doc/ subdirectory. + +Look in the doc/ subdirectory or read http://www.cubicweb.org/doc/en/ diff -r 9e639925ca2f -r ff6114c2c416 __init__.py --- a/__init__.py Tue Aug 04 11:43:03 2009 +0200 +++ b/__init__.py Tue Aug 04 15:06:09 2009 +0200 @@ -100,13 +100,27 @@ [(etype,)]) return self.decorate_rset(rset) + def empty_rset(self): + """return a result set for the given eid without doing actual query + (we have the eid, we can suppose it exists and user has access to the + entity) + """ + from cubicweb.rset import ResultSet + return self.decorate_rset(ResultSet([], 'Any X WHERE X eid -1')) + def entity_from_eid(self, eid, etype=None): - rset = self.eid_rset(eid, etype) - if rset: - return rset.get_entity(0, 0) - else: - return None + try: + return self.entity_cache(eid) + except KeyError: + rset = self.eid_rset(eid, etype) + entity = rset.get_entity(0, 0) + self.set_entity_cache(entity) + return entity + def entity_cache(self, eid): + raise KeyError + def set_entity_cache(self, entity): + pass # url generation methods ################################################## def build_url(self, *args, **kwargs): @@ -198,7 +212,7 @@ # abstract methods to override according to the web front-end ############# def base_url(self): - """return the root url of the application""" + """return the root url of the instance""" raise NotImplementedError def decorate_rset(self, rset): @@ -300,3 +314,6 @@ except AttributeError: return neg_role(obj.role) +def underline_title(title, car='-'): + return title+'\n'+(car*len(title)) + diff -r 9e639925ca2f -r ff6114c2c416 __pkginfo__.py --- a/__pkginfo__.py Tue Aug 04 11:43:03 2009 +0200 +++ b/__pkginfo__.py Tue Aug 04 15:06:09 2009 +0200 @@ -7,7 +7,7 @@ distname = "cubicweb" modname = "cubicweb" -numversion = (3, 3, 0) +numversion = (3, 4, 0) version = '.'.join(str(num) for num in numversion) license = 'LGPL v2' @@ -32,6 +32,13 @@ ftp = 'ftp://ftp.logilab.org/pub/cubicweb' pyversions = ['2.4', '2.5'] +classifiers = [ + 'Environment :: Web Environment', + 'Framework :: CubicWeb', + 'Programming Language :: Python', + 'Programming Language :: JavaScript', +] + import sys from os import listdir, environ diff -r 9e639925ca2f -r ff6114c2c416 _exceptions.py --- a/_exceptions.py Tue Aug 04 11:43:03 2009 +0200 +++ b/_exceptions.py Tue Aug 04 15:06:09 2009 +0200 @@ -129,6 +129,9 @@ class UnknownProperty(RegistryException): """property found in database but unknown in registry""" +class RegistryOutOfDate(RegistryException): + """raised when a source file modification is detected""" + # query exception ############################################################# class QueryError(CubicWebRuntimeError): diff -r 9e639925ca2f -r ff6114c2c416 appobject.py --- a/appobject.py Tue Aug 04 11:43:03 2009 +0200 +++ b/appobject.py Tue Aug 04 15:06:09 2009 +0200 @@ -10,7 +10,7 @@ from datetime import datetime, timedelta, time from logilab.common.decorators import classproperty -from logilab.common.deprecation import obsolete +from logilab.common.deprecation import deprecated from rql.nodes import VariableRef, SubQuery from rql.stmts import Union, Select @@ -25,8 +25,9 @@ class Cache(dict): def __init__(self): super(Cache, self).__init__() - self.cache_creation_date = None - self.latest_cache_lookup = datetime.now() + _now = datetime.now() + self.cache_creation_date = _now + self.latest_cache_lookup = _now CACHE_REGISTRY = {} @@ -39,11 +40,11 @@ At registration time, the following attributes are set on the class: :vreg: - the application's registry + the instance's registry :schema: - the application's schema + the instance's schema :config: - the application's configuration + the instance's configuration At instantiation time, the following attributes are set on the instance: :req: @@ -100,7 +101,7 @@ return '%s.%s.%s' % (cls.__registry__, cls.id, propid) @classproperty - @obsolete('use __select__ and & or | operators') + @deprecated('use __select__ and & or | operators') def __selectors__(cls): selector = cls.__select__ if isinstance(selector, AndSelector): @@ -127,8 +128,7 @@ if cachename in CACHE_REGISTRY: cache = CACHE_REGISTRY[cachename] else: - cache = Cache() - CACHE_REGISTRY[cachename] = cache + cache = CACHE_REGISTRY[cachename] = Cache() _now = datetime.now() if _now > cache.latest_cache_lookup + ONESECOND: ecache = self.req.execute('Any C,T WHERE C is CWCache, C name %(name)s, C timestamp T', @@ -276,7 +276,7 @@ return output.getvalue() def format_date(self, date, date_format=None, time=False): - """return a string for a date time according to application's + """return a string for a date time according to instance's configuration """ if date: @@ -289,7 +289,7 @@ return u'' def format_time(self, time): - """return a string for a time according to application's + """return a string for a time according to instance's configuration """ if time: @@ -297,7 +297,7 @@ return u'' def format_float(self, num): - """return a string for floating point number according to application's + """return a string for floating point number according to instance's configuration """ if num: diff -r 9e639925ca2f -r ff6114c2c416 common/__init__.py --- a/common/__init__.py Tue Aug 04 11:43:03 2009 +0200 +++ b/common/__init__.py Tue Aug 04 15:06:09 2009 +0200 @@ -18,9 +18,9 @@ rtype = 'String' @classmethod - def st_description(cls, funcnode): - return ', '.join(term.get_description() - for term in iter_funcnode_variables(funcnode)) + def st_description(cls, funcnode, mainindex, tr): + return ', '.join(sorted(term.get_description(mainindex, tr) + for term in iter_funcnode_variables(funcnode))) register_function(COMMA_JOIN) # XXX do not expose? @@ -41,8 +41,8 @@ rtype = 'String' @classmethod - def st_description(cls, funcnode): - return funcnode.children[0].get_description() + def st_description(cls, funcnode, mainindex, tr): + return funcnode.children[0].get_description(mainindex, tr) register_function(LIMIT_SIZE) diff -r 9e639925ca2f -r ff6114c2c416 common/i18n.py --- a/common/i18n.py Tue Aug 04 11:43:03 2009 +0200 +++ b/common/i18n.py Tue Aug 04 15:06:09 2009 +0200 @@ -63,7 +63,7 @@ """generate .mo files for a set of languages into the `destdir` i18n directory """ from logilab.common.fileutils import ensure_fs_mode - print 'compiling %s catalogs...' % destdir + print '-> compiling %s catalogs...' % destdir errors = [] for lang in langs: langdir = join(destdir, lang, 'LC_MESSAGES') @@ -73,7 +73,7 @@ pofiles = [pof for pof in pofiles if exists(pof)] mergedpo = join(destdir, '%s_merged.po' % lang) try: - # merge application messages' catalog with the stdlib's one + # merge instance/cubes messages catalogs with the stdlib's one execute('msgcat --use-first --sort-output --strict %s > %s' % (' '.join(pofiles), mergedpo)) # make sure the .mo file is writeable and compile with *msgfmt* diff -r 9e639925ca2f -r ff6114c2c416 common/mail.py --- a/common/mail.py Tue Aug 04 11:43:03 2009 +0200 +++ b/common/mail.py Tue Aug 04 15:06:09 2009 +0200 @@ -18,8 +18,8 @@ def addrheader(uaddr, uname=None): # even if an email address should be ascii, encode it using utf8 since - # application tests may generate non ascii email address - addr = uaddr.encode('UTF-8') + # automatic tests may generate non ascii email address + addr = uaddr.encode('UTF-8') if uname: return '%s <%s>' % (header(uname).encode(), addr) return addr diff -r 9e639925ca2f -r ff6114c2c416 common/migration.py --- a/common/migration.py Tue Aug 04 11:43:03 2009 +0200 +++ b/common/migration.py Tue Aug 04 15:06:09 2009 +0200 @@ -1,5 +1,4 @@ -"""utility to ease migration of application version to newly installed -version +"""utilities for instances migration :organization: Logilab :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. @@ -11,29 +10,14 @@ 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 from logilab.common.configuration import REQUIRED, read_old_config - +from logilab.common.shellutils import ASK -def migration_files(config, toupgrade): - """return an orderer list of path of scripts to execute to upgrade - an installed application according to installed cube and cubicweb versions - """ - merged = [] - for cube, fromversion, toversion in toupgrade: - if cube == 'cubicweb': - migrdir = config.migration_scripts_dir() - else: - migrdir = config.cube_migration_scripts_dir(cube) - scripts = filter_scripts(config, migrdir, fromversion, toversion) - merged += [s[1] for s in scripts] - if config.accept_mode('Any'): - migrdir = config.migration_scripts_dir() - merged.insert(0, join(migrdir, 'bootstrapmigration_repository.py')) - return merged +from cubicweb import ConfigurationError def filter_scripts(config, directory, fromversion, toversion, quiet=True): @@ -86,11 +70,10 @@ ability to show the script's content """ while True: - confirm = raw_input('** execute %r (Y/n/s[how]) ?' % scriptpath) - confirm = confirm.strip().lower() - if confirm in ('n', 'no'): + answer = ASK.ask('Execute %r ?' % scriptpath, ('Y','n','show'), 'Y') + if answer == 'n': return False - elif confirm in ('s', 'show'): + elif answer == 'show': stream = open(scriptpath) scriptcontent = stream.read() stream.close() @@ -124,6 +107,18 @@ 'interactive_mode': interactive, } + def __getattribute__(self, name): + try: + return object.__getattribute__(self, name) + except AttributeError: + cmd = 'cmd_%s' % name + if hasattr(self, cmd): + meth = getattr(self, cmd) + return lambda *args, **kwargs: self.interact(args, kwargs, + meth=meth) + raise + raise AttributeError(name) + def repo_connect(self): return self.config.repository() @@ -142,34 +137,42 @@ return False return orig_accept_mode(mode) self.config.accept_mode = accept_mode - scripts = migration_files(self.config, toupgrade) - if scripts: - vmap = dict( (pname, (fromver, tover)) for pname, fromver, tover in toupgrade) - self.__context.update({'applcubicwebversion': vcconf['cubicweb'], - 'cubicwebversion': self.config.cubicweb_version(), - 'versions_map': vmap}) - self.scripts_session(scripts) - else: - print 'no migration script to execute' + # may be an iterator + toupgrade = tuple(toupgrade) + vmap = dict( (cube, (fromver, tover)) for cube, fromver, tover in toupgrade) + ctx = self.__context + ctx['versions_map'] = vmap + if self.config.accept_mode('Any') and 'cubicweb' in vmap: + migrdir = self.config.migration_scripts_dir() + self.process_script(join(migrdir, 'bootstrapmigration_repository.py')) + for cube, fromversion, toversion in toupgrade: + if cube == 'cubicweb': + migrdir = self.config.migration_scripts_dir() + else: + migrdir = self.config.cube_migration_scripts_dir(cube) + scripts = filter_scripts(self.config, migrdir, fromversion, toversion) + if scripts: + prevversion = None + for version, script in scripts: + # take care to X.Y.Z_Any.py / X.Y.Z_common.py: we've to call + # cube_upgraded once all script of X.Y.Z have been executed + if prevversion is not None and version != prevversion: + self.cube_upgraded(cube, version) + prevversion = version + self.process_script(script) + self.cube_upgraded(cube, toversion) + else: + self.cube_upgraded(cube, toversion) + + def cube_upgraded(self, cube, version): + pass def shutdown(self): pass - def __getattribute__(self, name): - try: - return object.__getattribute__(self, name) - except AttributeError: - cmd = 'cmd_%s' % name - if hasattr(self, cmd): - meth = getattr(self, cmd) - return lambda *args, **kwargs: self.interact(args, kwargs, - meth=meth) - raise - raise AttributeError(name) - def interact(self, args, kwargs, meth): """execute the given method according to user's confirmation""" - msg = 'execute command: %s(%s) ?' % ( + msg = 'Execute command: %s(%s) ?' % ( meth.__name__[4:], ', '.join([repr(arg) for arg in args] + ['%s=%r' % (n,v) for n,v in kwargs.items()])) @@ -185,27 +188,24 @@ if `retry` is true the r[etry] answer may return 2 """ - print question, - possibleanswers = 'Y/n' + possibleanswers = ['Y','n'] if abort: - possibleanswers += '/a[bort]' + possibleanswers.append('abort') if shell: - possibleanswers += '/s[hell]' + possibleanswers.append('shell') if retry: - possibleanswers += '/r[etry]' + possibleanswers.append('retry') try: - confirm = raw_input('(%s): ' % ( possibleanswers, )) - answer = confirm.strip().lower() + answer = ASK.ask(question, possibleanswers, 'Y') except (EOFError, KeyboardInterrupt): answer = 'abort' - if answer in ('n', 'no'): + if answer == 'n': return False - if answer in ('r', 'retry'): + if answer == 'retry': return 2 - if answer in ('a', 'abort'): - self.rollback() + if answer == 'abort': raise SystemExit(1) - if shell and answer in ('s', 'shell'): + if shell and answer == 'shell': self.interactive_shell() return self.confirm(question) return True @@ -282,16 +282,6 @@ return None return func(*args, **kwargs) - def scripts_session(self, migrscripts): - """execute some scripts in a transaction""" - try: - for migrscript in migrscripts: - self.process_script(migrscript) - self.commit() - except: - self.rollback() - raise - def cmd_option_renamed(self, oldname, newname): """a configuration option has been renamed""" self._option_changes.append(('renamed', oldname, newname)) @@ -328,18 +318,18 @@ self.config.add_cubes(newcubes) return newcubes - def cmd_remove_cube(self, cube): + def cmd_remove_cube(self, cube, removedeps=False): + if removedeps: + toremove = self.config.expand_cubes([cube]) + else: + toremove = (cube,) origcubes = self.config._cubes - basecubes = list(origcubes) - for pkg in self.config.expand_cubes([cube]): - try: - basecubes.remove(pkg) - except ValueError: - continue + basecubes = [c for c in origcubes if not c in toremove] self.config._cubes = tuple(self.config.expand_cubes(basecubes)) removed = [p for p in origcubes if not p in self.config._cubes] - assert cube in removed, \ - "can't remove cube %s, used as a dependancy" % cube + if not cube in removed: + raise ConfigurationError("can't remove cube %s, " + "used as a dependency" % cube) return removed def rewrite_configuration(self): @@ -348,7 +338,7 @@ configfile = self.config.main_config_file() if self._option_changes: read_old_config(self.config, self._option_changes, configfile) - newconfig = mktemp() + _, newconfig = tempfile.mkstemp() for optdescr in self._option_changes: if optdescr[0] == 'added': optdict = self.config.get_option_def(optdescr[1]) diff -r 9e639925ca2f -r ff6114c2c416 common/mixins.py --- a/common/mixins.py Tue Aug 04 11:43:03 2009 +0200 +++ b/common/mixins.py Tue Aug 04 15:06:09 2009 +0200 @@ -8,7 +8,7 @@ """ __docformat__ = "restructuredtext en" -from logilab.common.deprecation import obsolete +from logilab.common.deprecation import deprecated from logilab.common.decorators import cached from cubicweb import typed_eid @@ -191,11 +191,18 @@ return rset.get_entity(0, 0) return None - def change_state(self, stateeid, trcomment=None, trcommentformat=None): + def change_state(self, state, trcomment=None, trcommentformat=None): """change the entity's state according to a state defined in given parameters """ - assert not isinstance(stateeid, basestring), 'change_state wants a state eid' + if isinstance(state, basestring): + state = self.wf_state(state) + assert state is not None, 'not a %s state: %s' % (self.id, state) + if hasattr(state, 'eid'): + stateeid = state.eid + else: + stateeid = state + stateeid = typed_eid(stateeid) if trcomment: self.req.set_shared_data('trcomment', trcomment) if trcommentformat: @@ -235,7 +242,7 @@ # specific vocabulary methods ############################################# - @obsolete('use EntityFieldsForm.subject_in_state_vocabulary') + @deprecated('use EntityFieldsForm.subject_in_state_vocabulary') def subject_in_state_vocabulary(self, rschema, limit=None): form = self.vreg.select('forms', 'edition', self.req, entity=self) return form.subject_in_state_vocabulary(rschema, limit) diff -r 9e639925ca2f -r ff6114c2c416 common/mttransforms.py --- a/common/mttransforms.py Tue Aug 04 11:43:03 2009 +0200 +++ b/common/mttransforms.py Tue Aug 04 15:06:09 2009 +0200 @@ -46,8 +46,8 @@ from cubicweb.ext.tal import compile_template except ImportError: HAS_TAL = False - from cubicweb.schema import FormatConstraint - FormatConstraint.need_perm_formats.remove('text/cubicweb-page-template') + from cubicweb import schema + schema.NEED_PERM_FORMATS.remove('text/cubicweb-page-template') else: HAS_TAL = True diff -r 9e639925ca2f -r ff6114c2c416 common/tags.py --- a/common/tags.py Tue Aug 04 11:43:03 2009 +0200 +++ b/common/tags.py Tue Aug 04 15:06:09 2009 +0200 @@ -7,7 +7,7 @@ """ __docformat__ = "restructuredtext en" -from cubicweb.common.uilib import simple_sgml_tag +from cubicweb.common.uilib import simple_sgml_tag, sgml_attributes class tag(object): def __init__(self, name, escapecontent=True): @@ -38,8 +38,7 @@ if id: attrs['id'] = id attrs['name'] = name - html = [u'' % sgml_attributes(attrs)] html += options html.append(u'') return u'\n'.join(html) diff -r 9e639925ca2f -r ff6114c2c416 common/test/unittest_migration.py --- a/common/test/unittest_migration.py Tue Aug 04 11:43:03 2009 +0200 +++ b/common/test/unittest_migration.py Tue Aug 04 15:06:09 2009 +0200 @@ -13,7 +13,8 @@ from cubicweb.devtools.apptest import TestEnvironment from cubicweb.cwconfig import CubicWebConfiguration -from cubicweb.common.migration import migration_files, filter_scripts +from cubicweb.common.migration import MigrationHelper, filter_scripts +from cubicweb.server.migractions import ServerMigrationHelper class Schema(dict): @@ -39,82 +40,53 @@ self.config.__class__.cubicweb_vobject_path = frozenset() self.config.__class__.cube_vobject_path = frozenset() - def test_migration_files_base(self): - self.assertListEquals(migration_files(self.config, [('cubicweb', (2,3,0), (2,4,0)), - ('TEMPLATE', (0,0,2), (0,0,3))]), - [SMIGRDIR+'bootstrapmigration_repository.py', - TMIGRDIR+'0.0.3_Any.py']) - self.assertListEquals(migration_files(self.config, [('cubicweb', (2,4,0), (2,5,0)), - ('TEMPLATE', (0,0,2), (0,0,3))]), - [SMIGRDIR+'bootstrapmigration_repository.py', - SMIGRDIR+'2.5.0_Any.sql', - TMIGRDIR+'0.0.3_Any.py']) - self.assertListEquals(migration_files(self.config, [('cubicweb', (2,5,0), (2,6,0)), - ('TEMPLATE', (0,0,3), (0,0,4))]), - [SMIGRDIR+'bootstrapmigration_repository.py', - SMIGRDIR+'2.6.0_Any.sql', - TMIGRDIR+'0.0.4_Any.py']) + def test_filter_scripts_base(self): + self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,3,0), (2,4,0)), + []) + self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,4,0), (2,5,0)), + [((2, 5, 0), SMIGRDIR+'2.5.0_Any.sql')]) + self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,0), (2,6,0)), + [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql')]) + self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,4,0), (2,6,0)), + [((2, 5, 0), SMIGRDIR+'2.5.0_Any.sql'), + ((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql')]) + self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,0), (2,5,1)), + []) + self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,0), (2,10,2)), + [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql'), + ((2, 10, 2), SMIGRDIR+'2.10.2_Any.sql')]) + self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,1), (2,6,0)), + [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql')]) -## def test_migration_files_overlap(self): -## self.assertListEquals(migration_files(self.config, (2,4,0), (2,10,2), -## (0,0,2), (0,1,2)), -## [SMIGRDIR+'bootstrapmigration_repository.py', -## TMIGRDIR+'0.0.3_Any.py', -## TMIGRDIR+'0.0.4_Any.py', -## SMIGRDIR+'2.4.0_2.5.0_Any.sql', -## SMIGRDIR+'2.5.1_2.6.0_Any.sql', -## TMIGRDIR+'0.1.0_Any.py', -## TMIGRDIR+'0.1.0_common.py', -## TMIGRDIR+'0.1.0_repository.py', -## TMIGRDIR+'0.1.2_Any.py', -## SMIGRDIR+'2.10.1_2.10.2_Any.sql']) + self.assertListEquals(filter_scripts(self.config, TMIGRDIR, (0,0,2), (0,0,3)), + [((0, 0, 3), TMIGRDIR+'0.0.3_Any.py')]) + self.assertListEquals(filter_scripts(self.config, TMIGRDIR, (0,0,2), (0,0,4)), + [((0, 0, 3), TMIGRDIR+'0.0.3_Any.py'), + ((0, 0, 4), TMIGRDIR+'0.0.4_Any.py')]) - def test_migration_files_for_mode(self): - from cubicweb.server.migractions import ServerMigrationHelper + def test_filter_scripts_for_mode(self): self.assertIsInstance(self.config.migration_handler(), ServerMigrationHelper) - from cubicweb.common.migration import MigrationHelper config = CubicWebConfiguration('data') config.verbosity = 0 self.assert_(not isinstance(config.migration_handler(), ServerMigrationHelper)) self.assertIsInstance(config.migration_handler(), MigrationHelper) config = self.config config.__class__.name = 'twisted' - self.assertListEquals(migration_files(config, [('TEMPLATE', (0,0,4), (0,1,0))]), - [TMIGRDIR+'0.1.0_common.py', - TMIGRDIR+'0.1.0_web.py']) - config.__class__.name = 'repository' - self.assertListEquals(migration_files(config, [('TEMPLATE', (0,0,4), (0,1,0))]), - [SMIGRDIR+'bootstrapmigration_repository.py', - TMIGRDIR+'0.1.0_Any.py', - TMIGRDIR+'0.1.0_common.py', - TMIGRDIR+'0.1.0_repository.py']) - config.__class__.name = 'all-in-one' - self.assertListEquals(migration_files(config, [('TEMPLATE', (0,0,4), (0,1,0))]), - [SMIGRDIR+'bootstrapmigration_repository.py', - TMIGRDIR+'0.1.0_Any.py', - TMIGRDIR+'0.1.0_common.py', - TMIGRDIR+'0.1.0_repository.py', - TMIGRDIR+'0.1.0_web.py']) + self.assertListEquals(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)), + [((0, 1 ,0), TMIGRDIR+'0.1.0_common.py'), + ((0, 1 ,0), TMIGRDIR+'0.1.0_web.py')]) config.__class__.name = 'repository' - - def test_filter_scripts(self): - self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,4,0), (2,5,0)), - [((2, 5, 0), SMIGRDIR+'2.5.0_Any.sql')]) - self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,4,0), (2,6,0)), - [((2, 5, 0), SMIGRDIR+'2.5.0_Any.sql'), - ((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql')]) - self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,0), (2,5,1)), - []) - self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,0), (2,6,0)), - [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql')]) - self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,0), (2,10,2)), - [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql'), - ((2, 10, 2), SMIGRDIR+'2.10.2_Any.sql')]) - self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,1), (2,6,0)), - [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql')]) - self.assertListEquals(filter_scripts(self.config, SMIGRDIR, (2,5,1), (2,10,2)), - [((2, 6, 0), SMIGRDIR+'2.6.0_Any.sql'), - ((2, 10, 2), SMIGRDIR+'2.10.2_Any.sql')]) + self.assertListEquals(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)), + [((0, 1 ,0), TMIGRDIR+'0.1.0_Any.py'), + ((0, 1 ,0), TMIGRDIR+'0.1.0_common.py'), + ((0, 1 ,0), TMIGRDIR+'0.1.0_repository.py')]) + config.__class__.name = 'all-in-one' + self.assertListEquals(filter_scripts(config, TMIGRDIR, (0,0,4), (0,1,0)), + [((0, 1 ,0), TMIGRDIR+'0.1.0_Any.py'), + ((0, 1 ,0), TMIGRDIR+'0.1.0_common.py'), + ((0, 1 ,0), TMIGRDIR+'0.1.0_repository.py'), + ((0, 1 ,0), TMIGRDIR+'0.1.0_web.py')]) + config.__class__.name = 'repository' from cubicweb.devtools import ApptestConfiguration, init_test_database, cleanup_sqlite diff -r 9e639925ca2f -r ff6114c2c416 common/uilib.py --- a/common/uilib.py Tue Aug 04 11:43:03 2009 +0200 +++ b/common/uilib.py Tue Aug 04 15:06:09 2009 +0200 @@ -15,7 +15,7 @@ from urllib import quote as urlquote from StringIO import StringIO -from logilab.mtconverter import html_escape, html_unescape +from logilab.mtconverter import xml_escape, html_unescape from cubicweb.utils import ustrftime @@ -66,7 +66,7 @@ except ImportError: def rest_publish(entity, data): """default behaviour if docutils was not found""" - return html_escape(data) + return xml_escape(data) TAG_PROG = re.compile(r'', re.U) def remove_html_tags(text): @@ -92,7 +92,9 @@ # fallback implementation, nicer one defined below if lxml is available def soup2xhtml(data, encoding): - return data + # normalize line break + # see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1 + return u'\n'.join(data.splitlines()) # fallback implementation, nicer one defined below if lxml> 2.0 is available def safe_cut(text, length): @@ -106,7 +108,7 @@ if len(text_nohtml) <= length: return text # else if un-tagged text is too long, cut it - return html_escape(text_nohtml[:length] + u'...') + return xml_escape(text_nohtml[:length] + u'...') fallback_safe_cut = safe_cut @@ -123,6 +125,10 @@ Note: the function considers a string with no surrounding tag as valid if
`data`
can be parsed by an XML parser """ + # normalize line break + # see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1 + data = u'\n'.join(data.splitlines()) + # XXX lxml 1.1 support still needed ? xmltree = etree.HTML('
%s
' % data) # NOTE: lxml 1.1 (etch platforms) doesn't recognize # the encoding=unicode parameter (lxml 2.0 does), this is @@ -203,10 +209,18 @@ # HTML generation helper functions ############################################ +HTML4_EMPTY_TAGS = frozenset(('base', 'meta', 'link', 'hr', 'br', 'param', + 'img', 'area', 'input', 'col')) + +def sgml_attributes(attrs): + return u' '.join(u'%s="%s"' % (attr, xml_escape(unicode(value))) + for attr, value in sorted(attrs.items()) + if value is not None) + 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: @@ -214,15 +228,16 @@ attrs['class'] = attrs.pop('klass') except KeyError: pass - value += u' ' + u' '.join(u'%s="%s"' % (attr, html_escape(unicode(value))) - for attr, value in sorted(attrs.items()) - if value is not None) + value += u' ' + sgml_attributes(attrs) if content: if escapecontent: - content = html_escape(unicode(content)) + content = xml_escape(unicode(content)) value += u'>%s' % (content, tag) else: - value += u'/>' + if tag in HTML4_EMPTY_TAGS: + value += u' />' + else: + value += u'>' % tag return value def tooltipize(text, tooltip, url=None): @@ -400,9 +415,9 @@ strings.append(body) strings.append(u'') if title: - strings.append(u'

%s

'% html_escape(title)) + strings.append(u'

%s

'% xml_escape(title)) try: - strings.append(u'

%s

' % html_escape(str(exception)).replace("\n","
")) + strings.append(u'

%s

' % xml_escape(str(exception)).replace("\n","
")) except UnicodeError: pass strings.append(u'
') @@ -410,9 +425,9 @@ strings.append(u'File %s, line ' u'%s, function ' u'%s:
'%( - html_escape(stackentry[0]), stackentry[1], html_escape(stackentry[2]))) + xml_escape(stackentry[0]), stackentry[1], xml_escape(stackentry[2]))) if stackentry[3]: - string = html_escape(stackentry[3]).decode('utf-8', 'replace') + string = xml_escape(stackentry[3]).decode('utf-8', 'replace') strings.append(u'  %s
\n' % (string)) # add locals info for each entry try: @@ -420,7 +435,7 @@ html_info = [] chars = 0 for name, value in local_context.iteritems(): - value = html_escape(repr(value)) + value = xml_escape(repr(value)) info = u'%s=%s, ' % (name, value) line_length = len(name) + len(value) chars += line_length @@ -485,5 +500,5 @@ def newfunc(*args, **kwargs): ret = function(*args, **kwargs) assert isinstance(ret, basestring) - return html_escape(ret) + return xml_escape(ret) return newfunc diff -r 9e639925ca2f -r ff6114c2c416 cwconfig.py --- a/cwconfig.py Tue Aug 04 11:43:03 2009 +0200 +++ b/cwconfig.py Tue Aug 04 15:06:09 2009 +0200 @@ -17,9 +17,12 @@ import sys import os import logging +from smtplib import SMTP +from threading import Lock from os.path import exists, join, expanduser, abspath, normpath, basename, isdir from logilab.common.decorators import cached +from logilab.common.deprecation import deprecated from logilab.common.logging_ext import set_log_methods, init_log from logilab.common.configuration import (Configuration, Method, ConfigurationMixIn, merge_options) @@ -29,6 +32,8 @@ CONFIGURATIONS = [] +SMTP_LOCK = Lock() + class metaconfiguration(type): """metaclass to automaticaly register configuration""" @@ -145,7 +150,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: @@ -190,6 +195,12 @@ 'help': 'web server root url', 'group': 'main', 'inputlevel': 1, }), + ('allow-email-login', + {'type' : 'yn', + 'default': False, + 'help': 'allow users to login with their primary email if set', + 'group': 'main', 'inputlevel': 2, + }), ('use-request-subdomain', {'type' : 'yn', 'default': None, @@ -211,7 +222,7 @@ 'group': 'appobjects', 'inputlevel': 2, }), ) - # static and class methods used to get application independant resources ## + # static and class methods used to get instance independant resources ## @staticmethod def cubicweb_version(): @@ -237,7 +248,7 @@ @classmethod def i18n_lib_dir(cls): - """return application's i18n directory""" + """return instance's i18n directory""" if cls.mode in ('dev', 'test') and not os.environ.get('APYCOT_ROOT'): return join(CW_SOFTWARE_ROOT, 'i18n') return join(cls.shared_dir(), 'i18n') @@ -414,7 +425,7 @@ @classmethod def build_vregistry_path(cls, templpath, evobjpath=None, tvobjpath=None): """given a list of directories, return a list of sub files and - directories that should be loaded by the application objects registry. + directories that should be loaded by the instance objects registry. :param evobjpath: optional list of sub-directories (or files without the .py ext) of @@ -529,8 +540,10 @@ # for some commands (creation...) we don't want to initialize gettext set_language = True - # set this to true to avoid false error message while creating an application + # set this to true to avoid false error message while creating an instance creating = False + # set this to true to allow somethings which would'nt be possible + repairing = False options = CubicWebNoAppConfiguration.options + ( ('log-file', @@ -554,14 +567,14 @@ }), ('sender-name', {'type' : 'string', - 'default': Method('default_application_id'), + 'default': Method('default_instance_id'), 'help': 'name used as HELO name for outgoing emails from the \ repository.', 'group': 'email', 'inputlevel': 2, }), ('sender-addr', {'type' : 'string', - 'default': 'devel@logilab.fr', + 'default': 'cubicweb@mydomain.com', 'help': 'email address used as HELO address for outgoing emails from \ the repository', 'group': 'email', 'inputlevel': 1, @@ -571,49 +584,49 @@ @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): - """return a configuration instance for the given application identifier + """return a configuration instance for the given instance identifier """ - config = config or guess_configuration(cls.application_home(appid)) + config = config or guess_configuration(cls.instance_home(appid)) configcls = configuration_cls(config) return configcls(appid) @classmethod def possible_configurations(cls, appid): """return the name of possible configurations for the given - application id + instance id """ - home = cls.application_home(appid) + home = cls.instance_home(appid) return possible_configurations(home) @classmethod - def application_home(cls, appid): - """return the home directory of the application with the given - application id + def instance_home(cls, appid): + """return the home directory of the instance with the given + instance id """ home = join(cls.registry_dir(), appid) if not exists(home): - raise ConfigurationError('no such application %s (check it exists with "cubicweb-ctl list")' % appid) + raise ConfigurationError('no such instance %s (check it exists with "cubicweb-ctl list")' % appid) return home MODES = ('common', 'repository', 'Any', 'web') @@ -627,14 +640,14 @@ # default configuration methods ########################################### - def default_application_id(self): - """return the application identifier, useful for option which need this + def default_instance_id(self): + """return the instance identifier, useful for option which need this as default value """ return self.appid def default_log_file(self): - """return default path to the log file of the application'server""" + """return default path to the log file of the instance'server""" if self.mode == 'dev': basepath = '/tmp/%s-%s' % (basename(self.appid), self.name) path = basepath + '.log' @@ -650,10 +663,10 @@ return '/var/log/cubicweb/%s-%s.log' % (self.appid, self.name) def default_pid_file(self): - """return default path to the pid file of the application'server""" + """return default path to the pid file of the instance'server""" return join(self.runtime_dir(), '%s-%s.pid' % (self.appid, self.name)) - # instance methods used to get application specific resources ############# + # instance methods used to get instance specific resources ############# def __init__(self, appid): self.appid = appid @@ -712,7 +725,7 @@ self._cubes = self.reorder_cubes(list(self._cubes) + cubes) def main_config_file(self): - """return application's control configuration file""" + """return instance's control configuration file""" return join(self.apphome, '%s.conf' % self.name) def save(self): @@ -729,7 +742,7 @@ return md5.new(';'.join(infos)).hexdigest() def load_site_cubicweb(self): - """load (web?) application's specific site_cubicweb file""" + """load instance's specific site_cubicweb file""" for path in reversed([self.apphome] + self.cubes_path()): sitefile = join(path, 'site_cubicweb.py') if exists(sitefile) and not sitefile in self._site_loaded: @@ -743,7 +756,7 @@ self.warning('site_erudi.py is deprecated, should be renamed to site_cubicweb.py') def _load_site_cubicweb(self, sitefile): - context = {} + context = {'__file__': sitefile} execfile(sitefile, context, context) self.info('%s loaded', sitefile) # cube specific options @@ -752,7 +765,7 @@ self.load_defaults() def load_configuration(self): - """load application's configuration files""" + """load instance's configuration files""" super(CubicWebConfiguration, self).load_configuration() if self.apphome and self.set_language: # init gettext @@ -764,14 +777,14 @@ return self._logging_initialized = True CubicWebNoAppConfiguration.init_log(self, logthreshold, debug, - logfile=self.get('log-file')) + logfile=self.get('log-file')) # read a config file if it exists logconfig = join(self.apphome, 'logging.conf') if exists(logconfig): logging.fileConfig(logconfig) def available_languages(self, *args): - """return available translation for an application, by looking for + """return available translation for an instance, by looking for compiled catalog take *args to be usable as a vocabulary method @@ -827,8 +840,30 @@ sourcedirs.append(self.i18n_lib_dir()) return i18n.compile_i18n_catalogs(sourcedirs, i18ndir, langs) + def sendmails(self, msgs): + """msgs: list of 2-uple (message object, recipients)""" + server, port = self['smtp-host'], self['smtp-port'] + SMTP_LOCK.acquire() + try: + try: + smtp = SMTP(server, port) + except Exception, ex: + self.exception("can't connect to smtp server %s:%s (%s)", + server, port, ex) + return + heloaddr = '%s <%s>' % (self['sender-name'], self['sender-addr']) + for msg, recipients in msgs: + try: + smtp.sendmail(heloaddr, recipients, msg.as_string()) + except Exception, ex: + self.exception("error sending mail to %s (%s)", + recipients, ex) + smtp.close() + finally: + SMTP_LOCK.release() + set_log_methods(CubicWebConfiguration, logging.getLogger('cubicweb.configuration')) -# alias to get a configuration instance from an application id -application_configuration = CubicWebConfiguration.config_for - +# alias to get a configuration instance from an instance id +instance_configuration = CubicWebConfiguration.config_for +application_configuration = deprecated('use instance_configuration')(instance_configuration) diff -r 9e639925ca2f -r ff6114c2c416 cwctl.py --- a/cwctl.py Tue Aug 04 11:43:03 2009 +0200 +++ b/cwctl.py Tue Aug 04 15:06:09 2009 +0200 @@ -1,6 +1,6 @@ """%%prog %s [options] %s -CubicWeb main applications controller. +CubicWeb main instances controller. :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses %s""" @@ -9,10 +9,11 @@ from os.path import exists, join, isfile, isdir from logilab.common.clcommands import register_commands, pop_arg +from logilab.common.shellutils import ASK -from cubicweb import ConfigurationError, ExecutionError, BadCommandUsage +from cubicweb import ConfigurationError, ExecutionError, BadCommandUsage, underline_title from cubicweb.cwconfig import CubicWebConfiguration as cwcfg, CONFIGURATIONS -from cubicweb.toolsutils import Command, main_run, rm, create_dir, confirm +from cubicweb.toolsutils import Command, main_run, rm, create_dir def wait_process_end(pid, maxtry=10, waittime=1): """wait for a process to actually die""" @@ -45,11 +46,11 @@ return modes -class ApplicationCommand(Command): - """base class for command taking 0 to n application id as arguments - (0 meaning all registered applications) +class InstanceCommand(Command): + """base class for command taking 0 to n instance id as arguments + (0 meaning all registered instances) """ - arguments = '[...]' + arguments = '[...]' options = ( ("force", {'short': 'f', 'action' : 'store_true', @@ -84,7 +85,7 @@ return allinstances def run(self, args): - """run the _method on each argument (a list of application + """run the _method on each argument (a list of instance identifiers) """ if not args: @@ -102,29 +103,29 @@ for appid in args: if askconfirm: print '*'*72 - if not confirm('%s application %r ?' % (self.name, appid)): + if not confirm('%s instance %r ?' % (self.name, appid)): continue self.run_arg(appid) def run_arg(self, appid): - cmdmeth = getattr(self, '%s_application' % self.name) + cmdmeth = getattr(self, '%s_instance' % self.name) try: cmdmeth(appid) except (KeyboardInterrupt, SystemExit): print >> sys.stderr, '%s aborted' % self.name sys.exit(2) # specific error code except (ExecutionError, ConfigurationError), ex: - print >> sys.stderr, 'application %s not %s: %s' % ( + print >> sys.stderr, 'instance %s not %s: %s' % ( appid, self.actionverb, ex) except Exception, ex: import traceback traceback.print_exc() - print >> sys.stderr, 'application %s not %s: %s' % ( + print >> sys.stderr, 'instance %s not %s: %s' % ( appid, self.actionverb, ex) -class ApplicationCommandFork(ApplicationCommand): - """Same as `ApplicationCommand`, but command is forked in a new environment +class InstanceCommandFork(InstanceCommand): + """Same as `InstanceCommand`, but command is forked in a new environment for each argument """ @@ -136,22 +137,21 @@ for appid in args: if askconfirm: print '*'*72 - if not confirm('%s application %r ?' % (self.name, appid)): + if not confirm('%s instance %r ?' % (self.name, appid)): continue if forkcmd: status = system('%s %s' % (forkcmd, appid)) if status: - sys.exit(status) + print '%s exited with status %s' % (forkcmd, status) else: self.run_arg(appid) # base commands ############################################################### class ListCommand(Command): - """List configurations, componants and applications. + """List configurations, cubes and instances. - list available configurations, installed web and server componants, and - registered applications + list available configurations, installed cubes, and registered instances """ name = 'list' options = ( @@ -206,30 +206,30 @@ try: regdir = cwcfg.registry_dir() except ConfigurationError, ex: - print 'No application available:', ex + print 'No instance available:', ex print return instances = list_instances(regdir) if instances: - print 'Available applications (%s):' % regdir + print 'Available instances (%s):' % regdir for appid in instances: modes = cwcfg.possible_configurations(appid) if not modes: - print '* %s (BROKEN application, no configuration found)' % appid + print '* %s (BROKEN instance, no configuration found)' % appid continue print '* %s (%s)' % (appid, ', '.join(modes)) try: config = cwcfg.config_for(appid, modes[0]) except Exception, exc: - print ' (BROKEN application, %s)' % exc + print ' (BROKEN instance, %s)' % exc continue else: - print 'No application available in %s' % regdir + print 'No instance available in %s' % regdir print -class CreateApplicationCommand(Command): - """Create an application from a cube. This is an unified +class CreateInstanceCommand(Command): + """Create an instance from a cube. This is an unified command which can handle web / server / all-in-one installation according to available parts of the software library and of the desired cube. @@ -238,11 +238,11 @@ the name of cube to use (list available cube names using the "list" command). You can use several cubes by separating them using comma (e.g. 'jpl,eemail') - - an identifier for the application to create + + an identifier for the instance to create """ name = 'create' - arguments = ' ' + arguments = ' ' options = ( ("config-level", {'short': 'l', 'type' : 'int', 'metavar': '', @@ -255,7 +255,7 @@ {'short': 'c', 'type' : 'choice', 'metavar': '', 'choices': ('all-in-one', 'repository', 'twisted'), 'default': 'all-in-one', - 'help': 'installation type, telling which part of an application \ + 'help': 'installation type, telling which part of an instance \ should be installed. You can list available configurations using the "list" \ command. Default to "all-in-one", e.g. an installation embedding both the RQL \ repository and the web server.', @@ -265,15 +265,16 @@ def run(self, args): """run the command with its specific arguments""" - from logilab.common.textutils import get_csv + from logilab.common.textutils import splitstrip configname = self.config.config - cubes = get_csv(pop_arg(args, 1)) + cubes = splitstrip(pop_arg(args, 1)) appid = pop_arg(args) # get the configuration and helper cwcfg.creating = True config = cwcfg.config_for(appid, configname) config.set_language = False - config.init_cubes(config.expand_cubes(cubes)) + cubes = config.expand_cubes(cubes) + config.init_cubes(cubes) helper = self.config_helper(config) # check the cube exists try: @@ -284,21 +285,23 @@ print '\navailable cubes:', print ', '.join(cwcfg.available_cubes()) return - # create the registry directory for this application + # create the registry directory for this instance + print '\n'+underline_title('Creating the instance %s' % appid) create_dir(config.apphome) # load site_cubicweb from the cubes dir (if any) config.load_site_cubicweb() # cubicweb-ctl configuration - print '** application\'s %s configuration' % configname - print '-' * 72 + print '\n'+underline_title('Configuring the instance (%s.conf)' % configname) config.input_config('main', self.config.config_level) # configuration'specific stuff print helper.bootstrap(cubes, self.config.config_level) # write down configuration config.save() + print '-> generated %s' % config.main_config_file() # handle i18n files structure # in the first cube given + print '-> preparing i18n catalogs' from cubicweb.common import i18n langs = [lang for lang, _ in i18n.available_catalogs(join(templdirs[0], 'i18n'))] errors = config.i18ncompile(langs) @@ -308,35 +311,31 @@ 'continue anyway ?'): print 'creation not completed' return - # create the additional data directory for this application + # create the additional data directory for this instance if config.appdatahome != config.apphome: # true in dev mode create_dir(config.appdatahome) + create_dir(join(config.appdatahome, 'backup')) if config['uid']: from logilab.common.shellutils import chown # this directory should be owned by the uid of the server process print 'set %s as owner of the data directory' % config['uid'] chown(config.appdatahome, config['uid']) - print - print - print '*' * 72 - print 'application %s (%s) created in %r' % (appid, configname, - config.apphome) - print + print '\n-> creation done for %r.\n' % config.apphome helper.postcreate() -class DeleteApplicationCommand(Command): - """Delete an application. Will remove application's files and +class DeleteInstanceCommand(Command): + """Delete an instance. Will remove instance's files and unregister it. """ name = 'delete' - arguments = '' + arguments = '' options = () def run(self, args): """run the command with its specific arguments""" - appid = pop_arg(args, msg="No application specified !") + appid = pop_arg(args, msg="No instance specified !") configs = [cwcfg.config_for(appid, configname) for configname in cwcfg.possible_configurations(appid)] if not configs: @@ -355,16 +354,16 @@ if ex.errno != errno.ENOENT: raise confignames = ', '.join([config.name for config in configs]) - print 'application %s (%s) deleted' % (appid, confignames) + print '-> instance %s (%s) deleted.' % (appid, confignames) -# application commands ######################################################## +# instance commands ######################################################## -class StartApplicationCommand(ApplicationCommand): - """Start the given applications. If no application is given, start them all. +class StartInstanceCommand(InstanceCommand): + """Start the given instances. If no instance is given, start them all. - ... - identifiers of the applications to start. If no application is + ... + identifiers of the instances to start. If no instance is given, start them all. """ name = 'start' @@ -376,22 +375,32 @@ ("force", {'short': 'f', 'action' : 'store_true', 'default': False, - 'help': 'start the application even if it seems to be already \ + 'help': 'start the instance even if it seems to be already \ running.'}), ('profile', {'short': 'P', 'type' : 'string', 'metavar': '', 'default': None, 'help': 'profile code and use the specified file to store stats', }), + ('loglevel', + {'short': 'l', 'type' : 'choice', 'metavar': '', + 'default': None, 'choices': ('debug', 'info', 'warning', 'error'), + 'help': 'debug if -D is set, error otherwise', + }), ) - def start_application(self, appid): - """start the application's server""" + def start_instance(self, appid): + """start the instance's server""" # use get() since start may be used from other commands (eg upgrade) # without all options defined debug = self.get('debug') force = self.get('force') + loglevel = self.get('loglevel') config = cwcfg.config_for(appid) + if loglevel is not None: + loglevel = 'LOG_%s' % loglevel.upper() + config.global_set_option('log-threshold', loglevel) + config.init_log(loglevel, debug=debug, force=True) if self.get('profile'): config.global_set_option('profile', self.config.profile) helper = self.config_helper(config, cmdname='start') @@ -400,36 +409,27 @@ msg = "%s seems to be running. Remove %s by hand if necessary or use \ the --force option." raise ExecutionError(msg % (appid, pidf)) - command = helper.start_command(config, debug) - if debug: - print "starting server with command :" - print command - if system(command): - print 'an error occured while starting the application, not started' - print - return False - if not debug: - print 'application %s started' % appid + helper.start_command(config, debug) return True -class StopApplicationCommand(ApplicationCommand): - """Stop the given applications. +class StopInstanceCommand(InstanceCommand): + """Stop the given instances. - ... - identifiers of the applications to stop. If no application is + ... + identifiers of the instances to stop. If no instance is given, stop them all. """ name = 'stop' actionverb = 'stopped' def ordered_instances(self): - instances = super(StopApplicationCommand, self).ordered_instances() + instances = super(StopInstanceCommand, self).ordered_instances() instances.reverse() return instances - def stop_application(self, appid): - """stop the application's server""" + def stop_instance(self, appid): + """stop the instance's server""" config = cwcfg.config_for(appid) helper = self.config_helper(config, cmdname='stop') helper.poststop() # do this anyway @@ -460,15 +460,15 @@ except OSError: # already removed by twistd pass - print 'application %s stopped' % appid + print 'instance %s stopped' % appid -class RestartApplicationCommand(StartApplicationCommand, - StopApplicationCommand): - """Restart the given applications. +class RestartInstanceCommand(StartInstanceCommand, + StopInstanceCommand): + """Restart the given instances. - ... - identifiers of the applications to restart. If no application is + ... + identifiers of the instances to restart. If no instance is given, restart them all. """ name = 'restart' @@ -478,18 +478,18 @@ regdir = cwcfg.registry_dir() if not isfile(join(regdir, 'startorder')) or len(args) <= 1: # no specific startorder - super(RestartApplicationCommand, self).run_args(args, askconfirm) + super(RestartInstanceCommand, self).run_args(args, askconfirm) return print ('some specific start order is specified, will first stop all ' - 'applications then restart them.') + 'instances then restart them.') # get instances in startorder stopped = [] for appid in args: if askconfirm: print '*'*72 - if not confirm('%s application %r ?' % (self.name, appid)): + if not confirm('%s instance %r ?' % (self.name, appid)): continue - self.stop_application(appid) + self.stop_instance(appid) stopped.append(appid) forkcmd = [w for w in sys.argv if not w in args] forkcmd[1] = 'start' @@ -499,46 +499,46 @@ if status: sys.exit(status) - def restart_application(self, appid): - self.stop_application(appid) - if self.start_application(appid): - print 'application %s %s' % (appid, self.actionverb) + def restart_instance(self, appid): + self.stop_instance(appid) + if self.start_instance(appid): + print 'instance %s %s' % (appid, self.actionverb) -class ReloadConfigurationCommand(RestartApplicationCommand): - """Reload the given applications. This command is equivalent to a +class ReloadConfigurationCommand(RestartInstanceCommand): + """Reload the given instances. This command is equivalent to a restart for now. - ... - identifiers of the applications to reload. If no application is + ... + identifiers of the instances to reload. If no instance is given, reload them all. """ name = 'reload' - def reload_application(self, appid): - self.restart_application(appid) + def reload_instance(self, appid): + self.restart_instance(appid) -class StatusCommand(ApplicationCommand): - """Display status information about the given applications. +class StatusCommand(InstanceCommand): + """Display status information about the given instances. - ... - identifiers of the applications to status. If no application is - given, get status information about all registered applications. + ... + identifiers of the instances to status. If no instance is + given, get status information about all registered instances. """ name = 'status' options = () @staticmethod - def status_application(appid): - """print running status information for an application""" + def status_instance(appid): + """print running status information for an instance""" for mode in cwcfg.possible_configurations(appid): config = cwcfg.config_for(appid, mode) print '[%s-%s]' % (appid, mode), try: pidf = config['pid-file'] except KeyError: - print 'buggy application, pid file not specified' + print 'buggy instance, pid file not specified' continue if not exists(pidf): print "doesn't seem to be running" @@ -553,22 +553,22 @@ print "running with pid %s" % (pid) -class UpgradeApplicationCommand(ApplicationCommandFork, - StartApplicationCommand, - StopApplicationCommand): - """Upgrade an application after cubicweb and/or component(s) upgrade. +class UpgradeInstanceCommand(InstanceCommandFork, + StartInstanceCommand, + StopInstanceCommand): + """Upgrade an instance after cubicweb and/or component(s) upgrade. For repository update, you will be prompted for a login / password to use to connect to the system database. For some upgrades, the given user should have create or alter table permissions. - ... - identifiers of the applications to upgrade. If no application is + ... + identifiers of the instances to upgrade. If no instance is given, upgrade them all. """ name = 'upgrade' actionverb = 'upgraded' - options = ApplicationCommand.options + ( + options = InstanceCommand.options + ( ('force-componant-version', {'short': 't', 'type' : 'csv', 'metavar': 'cube1=X.Y.Z,cube2=X.Y.Z', 'default': None, @@ -586,7 +586,7 @@ ('nostartstop', {'short': 'n', 'action' : 'store_true', 'default': False, - 'help': 'don\'t try to stop application before migration and to restart it after.'}), + 'help': 'don\'t try to stop instance before migration and to restart it after.'}), ('verbosity', {'short': 'v', 'type' : 'int', 'metavar': '<0..2>', @@ -597,7 +597,7 @@ ('backup-db', {'short': 'b', 'type' : 'yn', 'metavar': '', 'default': None, - 'help': "Backup the application database before upgrade.\n"\ + 'help': "Backup the instance database before upgrade.\n"\ "If the option is ommitted, confirmation will be ask.", }), @@ -612,25 +612,24 @@ ) def ordered_instances(self): - # need this since mro return StopApplicationCommand implementation - return ApplicationCommand.ordered_instances(self) + # need this since mro return StopInstanceCommand implementation + return InstanceCommand.ordered_instances(self) - def upgrade_application(self, appid): + def upgrade_instance(self, appid): + print '\n' + underline_title('Upgrading the instance %s' % appid) from logilab.common.changelog import Version config = cwcfg.config_for(appid) - config.creating = True # notice we're not starting the server + config.repairing = True # notice we're not starting the server config.verbosity = self.config.verbosity try: config.set_sources_mode(self.config.ext_sources or ('migration',)) except AttributeError: # not a server config pass - # get application and installed versions for the server and the componants - print 'getting versions configuration from the repository...' + # get instance and installed versions for the server and the componants mih = config.migration_handler() repo = mih.repo_connect() vcconf = repo.get_versions() - print 'done' if self.config.force_componant_version: packversions = {} for vdef in self.config.force_componant_version: @@ -654,15 +653,15 @@ else: applcubicwebversion = vcconf.get('cubicweb') if cubicwebversion > applcubicwebversion: - toupgrade.append( ('cubicweb', applcubicwebversion, cubicwebversion) ) + toupgrade.append(('cubicweb', applcubicwebversion, cubicwebversion)) if not self.config.fs_only and not toupgrade: - print 'no software migration needed for application %s' % appid + print '-> no software migration needed for instance %s.' % appid return for cube, fromversion, toversion in toupgrade: - print '**** %s migration %s -> %s' % (cube, fromversion, toversion) + print '-> migration needed from %s to %s for %s' % (fromversion, toversion, cube) # only stop once we're sure we have something to do if not (cwcfg.mode == 'dev' or self.config.nostartstop): - self.stop_application(appid) + self.stop_instance(appid) # run cubicweb/componants migration scripts mih.migrate(vcconf, reversed(toupgrade), self.config) # rewrite main configuration file @@ -677,16 +676,15 @@ errors = config.i18ncompile(langs) if errors: print '\n'.join(errors) - if not confirm('error while compiling message catalogs, ' + if not confirm('Error while compiling message catalogs, ' 'continue anyway ?'): - print 'migration not completed' + print '-> migration not completed.' return - mih.rewrite_vcconfiguration() mih.shutdown() print - print 'application migrated' + print '-> instance migrated.' if not (cwcfg.mode == 'dev' or self.config.nostartstop): - self.start_application(appid) + self.start_instance(appid) print @@ -696,11 +694,11 @@ argument may be given corresponding to a file containing commands to execute in batch mode. - - the identifier of the application to connect. + + the identifier of the instance to connect. """ name = 'shell' - arguments = ' [batch command file]' + arguments = ' [batch command file]' options = ( ('system-only', {'short': 'S', 'action' : 'store_true', @@ -720,7 +718,7 @@ ) def run(self, args): - appid = pop_arg(args, 99, msg="No application specified !") + appid = pop_arg(args, 99, msg="No instance specified !") config = cwcfg.config_for(appid) if self.config.ext_sources: assert not self.config.system_only @@ -732,24 +730,25 @@ config.set_sources_mode(sources) mih = config.migration_handler() if args: - mih.scripts_session(args) + for arg in args: + mih.process_script(arg) else: mih.interactive_shell() mih.shutdown() -class RecompileApplicationCatalogsCommand(ApplicationCommand): - """Recompile i18n catalogs for applications. +class RecompileInstanceCatalogsCommand(InstanceCommand): + """Recompile i18n catalogs for instances. - ... - identifiers of the applications to consider. If no application is - given, recompile for all registered applications. + ... + identifiers of the instances to consider. If no instance is + given, recompile for all registered instances. """ name = 'i18ninstance' @staticmethod - def i18ninstance_application(appid): - """recompile application's messages catalogs""" + def i18ninstance_instance(appid): + """recompile instance's messages catalogs""" config = cwcfg.config_for(appid) try: config.bootstrap_cubes() @@ -758,8 +757,8 @@ if ex.errno != errno.ENOENT: raise # bootstrap_cubes files doesn't exist - # set creating to notify this is not a regular start - config.creating = True + # notify this is not a regular start + config.repairing = True # create an in-memory repository, will call config.init_cubes() config.repository() except AttributeError: @@ -793,16 +792,16 @@ print cube register_commands((ListCommand, - CreateApplicationCommand, - DeleteApplicationCommand, - StartApplicationCommand, - StopApplicationCommand, - RestartApplicationCommand, + CreateInstanceCommand, + DeleteInstanceCommand, + StartInstanceCommand, + StopInstanceCommand, + RestartInstanceCommand, ReloadConfigurationCommand, StatusCommand, - UpgradeApplicationCommand, + UpgradeInstanceCommand, ShellCommand, - RecompileApplicationCatalogsCommand, + RecompileInstanceCatalogsCommand, ListInstancesCommand, ListCubesCommand, )) diff -r 9e639925ca2f -r ff6114c2c416 cwvreg.py --- a/cwvreg.py Tue Aug 04 11:43:03 2009 +0200 +++ b/cwvreg.py Tue Aug 04 15:06:09 2009 +0200 @@ -9,11 +9,12 @@ _ = unicode from logilab.common.decorators import cached, clear_cache -from logilab.common.deprecation import obsolete +from logilab.common.deprecation import deprecated from rql import RQLHelper -from cubicweb import ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid +from cubicweb import (ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid, + RegistryOutOfDate) from cubicweb.vregistry import VRegistry, ObjectNotFound, NoSelectableObject from cubicweb.rtags import RTAGS @@ -40,15 +41,15 @@ class CubicWebRegistry(VRegistry): - """Central registry for the cubicweb application, extending the generic + """Central registry for the cubicweb instance, extending the generic VRegistry with some cubicweb specific stuff. - This is one of the central object in cubicweb application, coupling + This is one of the central object in cubicweb instance, 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): + by the web instance (library may use some others additional registries): * etypes * views @@ -91,11 +92,16 @@ self.register_property(key, **propdef) def set_schema(self, schema): - """set application'schema and load application objects""" + """set instance'schema and load application objects""" self.schema = schema clear_cache(self, 'rqlhelper') # now we can load application's web objects self.register_objects(self.config.vregistry_path()) + # map lowered entity type names to their actual name + self.case_insensitive_etypes = {} + for etype in self.schema.entities(): + etype = str(etype) + self.case_insensitive_etypes[etype.lower()] = etype def update_schema(self, schema): """update .schema attribute on registered objects, necessary for some @@ -134,6 +140,15 @@ def register_objects(self, path, force_reload=None): """overriden to remove objects requiring a missing interface""" + try: + self._register_objects(path, force_reload) + except RegistryOutOfDate: + # modification detected, reset and reload + self.reset() + self._register_objects(path, force_reload) + + def _register_objects(self, path, force_reload=None): + """overriden to remove objects requiring a missing interface""" extrapath = {} for cubesdir in self.config.cubes_search_path(): if cubesdir != self.config.CUBES_DIR: @@ -279,22 +294,22 @@ self.exception('error while trying to select %s view for %s', vid, rset) - @obsolete("use .select_object('boxes', ...)") + @deprecated("use .select_object('boxes', ...)") def select_box(self, oid, *args, **kwargs): """return the most specific view according to the result set""" return self.select_object('boxes', oid, *args, **kwargs) - @obsolete("use .select_object('components', ...)") + @deprecated("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', ...)") + @deprecated("use .select_object('actions', ...)") def select_action(self, oid, *args, **kwargs): """return the most specific view according to the result set""" return self.select_object('actions', oid, *args, **kwargs) - @obsolete("use .select('views', ...)") + @deprecated("use .select('views', ...)") def select_view(self, __vid, req, rset=None, **kwargs): """return the most specific view according to the result set""" return self.select('views', __vid, req, rset=rset, **kwargs) @@ -303,9 +318,10 @@ def user_property_keys(self, withsitewide=False): if withsitewide: - return sorted(self['propertydefs']) + return sorted(k for k in self['propertydefs'] + if not k.startswith('sources.')) return sorted(k for k, kd in self['propertydefs'].iteritems() - if not kd['sitewide']) + if not kd['sitewide'] and not k.startswith('sources.')) def register_property(self, key, type, help, default=None, vocabulary=None, sitewide=False): @@ -408,7 +424,7 @@ """return an instance of the most specific object according to parameters - raise NoSelectableObject if not object apply + raise NoSelectableObject if no object apply """ for vobjectcls in vobjects: self._fix_cls_attrs(vobjectcls) diff -r 9e639925ca2f -r ff6114c2c416 dbapi.py --- a/dbapi.py Tue Aug 04 11:43:03 2009 +0200 +++ b/dbapi.py Tue Aug 04 15:06:09 2009 +0200 @@ -13,6 +13,7 @@ from logging import getLogger from time import time, clock +from itertools import count from logilab.common.logging_ext import set_log_methods from cubicweb import ETYPE_NAME_MAP, ConnectionError, RequestSessionMixIn @@ -21,6 +22,12 @@ _MARKER = object() +def _fake_property_value(self, name): + try: + return super(dbapi.DBAPIRequest, self).property_value(name) + except KeyError: + return '' + class ConnectionProperties(object): def __init__(self, cnxtype=None, lang=None, close=True, log=False): self.cnxtype = cnxtype or 'pyro' @@ -60,22 +67,22 @@ raise ConnectionError('Could not get repository for %s ' '(not registered in Pyro), ' 'you may have to restart your server-side ' - 'application' % nsid) + 'instance' % nsid) return core.getProxyForURI(uri) -def repo_connect(repo, user, password, cnxprops=None): +def repo_connect(repo, login, password, cnxprops=None): """Constructor to create a new connection to the CubicWeb repository. Returns a Connection instance. """ cnxprops = cnxprops or ConnectionProperties('inmemory') - cnxid = repo.connect(unicode(user), password, cnxprops=cnxprops) + cnxid = repo.connect(unicode(login), password, cnxprops=cnxprops) cnx = Connection(repo, cnxid, cnxprops) if cnxprops.cnxtype == 'inmemory': cnx.vreg = repo.vreg return cnx -def connect(database=None, user=None, password=None, host=None, +def connect(database=None, login=None, password=None, host=None, group=None, cnxprops=None, port=None, setvreg=True, mulcnx=True, initlog=True): """Constructor for creating a connection to the CubicWeb repository. @@ -111,11 +118,11 @@ vreg.set_schema(schema) else: vreg = None - cnx = repo_connect(repo, user, password, cnxprops) + cnx = repo_connect(repo, login, password, cnxprops) cnx.vreg = vreg return cnx -def in_memory_cnx(config, user, password): +def in_memory_cnx(config, login, password): """usefull method for testing and scripting to get a dbapi.Connection object connected to an in-memory repository instance """ @@ -128,7 +135,7 @@ repo = get_repository('inmemory', config=config, vreg=vreg) # connection to the CubicWeb repository cnxprops = ConnectionProperties('inmemory') - cnx = repo_connect(repo, user, password, cnxprops=cnxprops) + cnx = repo_connect(repo, login, password, cnxprops=cnxprops) return repo, cnx @@ -176,7 +183,7 @@ except KeyError: # this occurs usually during test execution self._ = self.__ = unicode - self.debug('request language: %s', self.lang) + self.debug('request default language: %s', self.lang) def decorate_rset(self, rset): rset.vreg = self.vreg @@ -245,7 +252,7 @@ @property def user(self): if self._user is None and self.cnx: - self.set_user(self.cnx.user(self)) + self.set_user(self.cnx.user(self, {'lang': self.lang})) return self._user def set_user(self, user): @@ -367,6 +374,10 @@ """raise `BadSessionId` if the connection is no more valid""" self._repo.check_session(self.sessionid) + def set_session_props(self, **props): + """raise `BadSessionId` if the connection is no more valid""" + self._repo.set_session_props(self.sessionid, props) + def get_shared_data(self, key, default=None, pop=False): """return value associated to `key` in shared data""" return self._repo.get_shared_data(self.sessionid, key, default, pop) @@ -390,7 +401,8 @@ raise ProgrammingError('Closed connection') return self._repo.get_schema() - def load_vobjects(self, cubes=_MARKER, subpath=None, expand=True, force_reload=None): + def load_vobjects(self, cubes=_MARKER, subpath=None, expand=True, + force_reload=None): config = self.vreg.config if cubes is _MARKER: cubes = self._repo.get_cubes() @@ -418,10 +430,35 @@ hm, config = self._repo.hm, self._repo.config hm.set_schema(hm.schema) # reset structure hm.register_system_hooks(config) - # application specific hooks - if self._repo.config.application_hooks: + # instance specific hooks + if self._repo.config.instance_hooks: hm.register_hooks(config.load_hooks(self.vreg)) + def use_web_compatible_requests(self, baseurl, sitetitle=None): + """monkey patch DBAPIRequest to fake a cw.web.request, so you should + able to call html views using rset from a simple dbapi connection. + + You should call `load_vobjects` at some point to register those views. + """ + from cubicweb.web.request import CubicWebRequestBase as cwrb + DBAPIRequest.build_ajax_replace_url = cwrb.build_ajax_replace_url.im_func + DBAPIRequest.list_form_param = cwrb.list_form_param.im_func + DBAPIRequest.property_value = _fake_property_value + DBAPIRequest.next_tabindex = count().next + DBAPIRequest.form = {} + DBAPIRequest.data = {} + fake = lambda *args, **kwargs: None + DBAPIRequest.relative_path = fake + DBAPIRequest.url = fake + DBAPIRequest.next_tabindex = fake + DBAPIRequest.add_js = fake #cwrb.add_js.im_func + DBAPIRequest.add_css = fake #cwrb.add_css.im_func + # XXX could ask the repo for it's base-url configuration + self.vreg.config.set_option('base-url', baseurl) + # XXX why is this needed? if really needed, could be fetched by a query + if sitetitle is not None: + self.vreg['propertydefs']['ui.site-title'] = {'default': sitetitle} + def source_defs(self): """Return the definition of sources used by the repository. @@ -434,7 +471,8 @@ def user(self, req=None, props=None): """return the User object associated to this connection""" # cnx validity is checked by the call to .user_info - eid, login, groups, properties = self._repo.user_info(self.sessionid, props) + eid, login, groups, properties = self._repo.user_info(self.sessionid, + props) if req is None: req = self.request() rset = req.eid_rset(eid, 'CWUser') diff -r 9e639925ca2f -r ff6114c2c416 debian/changelog --- a/debian/changelog Tue Aug 04 11:43:03 2009 +0200 +++ b/debian/changelog Tue Aug 04 15:06:09 2009 +0200 @@ -1,3 +1,40 @@ +cubicweb (3.3.4-2) unstable; urgency=low + + * fix conflicting test files + + -- Sylvain Thénault Tue, 21 Jul 2009 23:09:03 +0200 + +cubicweb (3.3.4-1) unstable; urgency=low + + * new upstream release + + -- Sylvain Thénault Tue, 21 Jul 2009 13:11:38 +0200 + +cubicweb (3.3.3-2) unstable; urgency=low + + * re-release with "from __future__ import with_statement" commented out to + avoid broken installation if 2.4 is installed + + -- Sylvain Thénault Mon, 06 Jul 2009 17:33:15 +0200 + +cubicweb (3.3.3-1) unstable; urgency=low + + * new upstream release + + -- Sylvain Thénault Mon, 06 Jul 2009 13:24:29 +0200 + +cubicweb (3.3.2-1) unstable; urgency=low + + * new upstream release + + -- Sylvain Thénault Thu, 25 Jun 2009 07:58:14 +0200 + +cubicweb (3.3.1-1) unstable; urgency=low + + * new upstream release + + -- Aurélien Campéas Mon, 22 Jun 2009 12:00:00 +0200 + cubicweb (3.3.0-1) unstable; urgency=low * new upstream release diff -r 9e639925ca2f -r ff6114c2c416 debian/control --- a/debian/control Tue Aug 04 11:43:03 2009 +0200 +++ b/debian/control Tue Aug 04 15:06:09 2009 +0200 @@ -62,7 +62,7 @@ Architecture: all XB-Python-Version: ${python:Versions} Depends: ${python:Depends}, cubicweb-common (= ${source:Version}), python-simplejson (>= 1.3), python-elementtree -Recommends: python-docutils, python-vobject, fckeditor +Recommends: python-docutils, python-vobject, fckeditor, python-fyzz Description: web interface library for the CubicWeb framework CubicWeb is a semantic web application framework. . @@ -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.41.0), python-yams (>= 0.23.0), python-rql (>= 0.22.0) -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 9e639925ca2f -r ff6114c2c416 debian/cubicweb-dev.install.in --- a/debian/cubicweb-dev.install.in Tue Aug 04 11:43:03 2009 +0200 +++ b/debian/cubicweb-dev.install.in Tue Aug 04 15:06:09 2009 +0200 @@ -1,2 +1,11 @@ debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/devtools/ usr/lib/PY_VERSION/site-packages/cubicweb/ debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/skeleton/ usr/lib/PY_VERSION/site-packages/cubicweb/ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/test usr/lib/PY_VERSION/site-packages/cubicweb/ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/common/test usr/lib/PY_VERSION/site-packages/cubicweb/common/ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/entities/test usr/lib/PY_VERSION/site-packages/cubicweb/entities/ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/ext/test usr/lib/PY_VERSION/site-packages/cubicweb/ext/ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/server/test usr/lib/PY_VERSION/site-packages/cubicweb/server/ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/sobjects/test usr/lib/PY_VERSION/site-packages/cubicweb/sobjects/ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/web/test usr/lib/PY_VERSION/site-packages/cubicweb/web/ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/etwist/test usr/lib/PY_VERSION/site-packages/cubicweb/etwist/ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/goa/test usr/lib/PY_VERSION/site-packages/cubicweb/goa/ diff -r 9e639925ca2f -r ff6114c2c416 debian/rules --- a/debian/rules Tue Aug 04 11:43:03 2009 +0200 +++ b/debian/rules Tue Aug 04 15:06:09 2009 +0200 @@ -47,12 +47,13 @@ dh_lintian # Remove unittests directory (should be available in cubicweb-dev only) - rm -rf debian/cubicweb-server/usr/share/pyshared/cubicweb/server/test - rm -rf debian/cubicweb-server/usr/share/pyshared/cubicweb/sobjects/test - rm -rf debian/cubicweb-dev/usr/share/pyshared/cubicweb/devtools/test - rm -rf debian/cubicweb-web/usr/share/pyshared/cubicweb/web/test - rm -rf debian/cubicweb-common/usr/share/pyshared/cubicweb/common/test - rm -rf debian/cubicweb-common/usr/share/pyshared/cubicweb/entities/test + rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/server/test + rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/sobjects/test + rm -rf debian/cubicweb-web/usr/lib/${PY_VERSION}/site-packages/cubicweb/web/test + rm -rf debian/cubicweb-twisted/usr/lib/${PY_VERSION}/site-packages/cubicweb/etwist/test + rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/site-packages/cubicweb/ext/test + rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/site-packages/cubicweb/common/test + rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/site-packages/cubicweb/entities/test # cubes directory must be managed as a valid python module touch debian/cubicweb-common/usr/share/cubicweb/cubes/__init__.py diff -r 9e639925ca2f -r ff6114c2c416 devtools/__init__.py --- a/devtools/__init__.py Tue Aug 04 11:43:03 2009 +0200 +++ b/devtools/__init__.py Tue Aug 04 15:06:09 2009 +0200 @@ -43,7 +43,7 @@ class TestServerConfiguration(ServerConfiguration): mode = 'test' set_language = False - read_application_schema = False + read_instance_schema = False bootstrap_schema = False init_repository = True options = merge_options(ServerConfiguration.options + ( @@ -77,12 +77,12 @@ def apphome(self): if exists(self.appid): return abspath(self.appid) - # application cube test + # cube test return abspath('..') appdatahome = apphome def main_config_file(self): - """return application's control configuration file""" + """return instance's control configuration file""" return join(self.apphome, '%s.conf' % self.name) def instance_md5_version(self): @@ -149,7 +149,7 @@ return ('en', 'fr', 'de') def ext_resources_file(self): - """return application's external resources file""" + """return instance's external resources file""" return join(self.apphome, 'data', 'external_resources') def pyro_enabled(self): @@ -235,14 +235,14 @@ # XXX I'm afraid this test will prevent to run test from a production # environment self._sources = None - # application cube test + # instance cube test if cube is not None: self.apphome = self.cube_dir(cube) elif 'web' in os.getcwd().split(os.sep): # web test self.apphome = join(normpath(join(dirname(__file__), '..')), 'web') else: - # application cube test + # cube test self.apphome = abspath('..') self.sourcefile = sourcefile self.global_set_option('realm', '') diff -r 9e639925ca2f -r ff6114c2c416 devtools/apptest.py --- a/devtools/apptest.py Tue Aug 04 11:43:03 2009 +0200 +++ b/devtools/apptest.py Tue Aug 04 15:06:09 2009 +0200 @@ -1,4 +1,4 @@ -"""This module provides misc utilities to test applications +"""This module provides misc utilities to test instances :organization: Logilab :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. @@ -15,7 +15,7 @@ from logilab.common.pytest import nocoverage from logilab.common.umessage import message_from_string -from logilab.common.deprecation import deprecated_function +from logilab.common.deprecation import deprecated from cubicweb.devtools import init_test_database, TestServerConfiguration, ApptestConfiguration from cubicweb.devtools._apptest import TestEnvironment @@ -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')) @@ -46,12 +54,12 @@ def sendmail(self, helo_addr, recipients, msg): MAILBOX.append(Email(recipients, msg)) -from cubicweb.server import hookhelper -hookhelper.SMTP = MockSMTP +from cubicweb import cwconfig +cwconfig.SMTP = MockSMTP def get_versions(self, checkversions=False): - """return the a dictionary containing cubes used by this application + """return the a dictionary containing cubes used by this instance as key with their version as value, including cubicweb version. This is a public method, not requiring a session id. @@ -226,7 +234,7 @@ 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) + paddrelactions = deprecated()(pactions_by_cats) def pactionsdict(self, req, rset, skipcategories=('addrelated', 'siteactions', 'useractions')): res = {} @@ -398,7 +406,7 @@ rset.vreg = self.vreg rset.req = self.session # call to set_pool is necessary to avoid pb when using - # application entities for convenience + # instance entities for convenience self.session.set_pool() return rset @@ -449,7 +457,6 @@ pactionsdict = EnvBasedTC.pactionsdict.im_func # default test setup and teardown ######################################### - copy_schema = False def _prepare(self): MAILBOX[:] = [] # reset mailbox @@ -462,17 +469,6 @@ self.__close = repo.close self.cnxid = self.cnx.sessionid self.session = repo._sessions[self.cnxid] - # XXX copy schema since hooks may alter it and it may be not fully - # cleaned (missing some schema synchronization support) - try: - origschema = repo.__schema - except AttributeError: - origschema = repo.schema - repo.__schema = origschema - if self.copy_schema: - repo.schema = deepcopy(origschema) - repo.set_schema(repo.schema) # reset hooks - repo.vreg.update_schema(repo.schema) self.cnxs = [] # reset caches, they may introduce bugs among tests repo._type_source_cache = {} diff -r 9e639925ca2f -r ff6114c2c416 devtools/devctl.py --- a/devtools/devctl.py Tue Aug 04 11:43:03 2009 +0200 +++ b/devtools/devctl.py Tue Aug 04 15:06:09 2009 +0200 @@ -12,20 +12,20 @@ from datetime import datetime from os import mkdir, chdir from os.path import join, exists, abspath, basename, normpath, split, isdir - +from warnings import warn from logilab.common import STD_BLACKLIST from logilab.common.modutils import get_module_files -from logilab.common.textutils import get_csv +from logilab.common.textutils import splitstrip +from logilab.common.shellutils import ASK from logilab.common.clcommands import register_commands -from cubicweb import CW_SOFTWARE_ROOT as BASEDIR, BadCommandUsage +from cubicweb import CW_SOFTWARE_ROOT as BASEDIR, BadCommandUsage, underline_title from cubicweb.__pkginfo__ import version as cubicwebversion -from cubicweb.toolsutils import Command, confirm, copy_skeleton +from cubicweb.toolsutils import Command, copy_skeleton from cubicweb.web.webconfig import WebConfiguration from cubicweb.server.serverconfig import ServerConfiguration - class DevCubeConfiguration(ServerConfiguration, WebConfiguration): """dummy config to get full library schema and entities""" creating = True @@ -254,15 +254,14 @@ 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) - potfiles = [join(I18NDIR, 'entities.pot')] - print '******** extract schema messages' + tempdir = tempdir.mkdtemp() + potfiles = [join(I18NDIR, 'static-messages.pot')] + print '-> extract schema messages.' schemapot = join(tempdir, 'schema.pot') potfiles.append(schemapot) # explicit close necessary else the file may not be yet flushed when @@ -270,10 +269,10 @@ schemapotstream = file(schemapot, 'w') generate_schema_pot(schemapotstream.write, cubedir=None) schemapotstream.close() - print '******** extract TAL messages' + print '-> extract TAL messages.' tali18nfile = join(tempdir, 'tali18n.py') extract_from_tal(find(join(BASEDIR, 'web'), ('.py', '.pt')), tali18nfile) - print '******** .pot files generation' + print '-> generate .pot files.' for id, files, lang in [('pycubicweb', get_module_files(BASEDIR) + list(globfind(join(BASEDIR, 'misc', 'migration'), '*.py')), None), ('schemadescr', globfind(join(BASEDIR, 'schemas'), '*.py'), None), ('yams', get_module_files(yams.__path__[0]), None), @@ -288,11 +287,11 @@ if exists(potfile): potfiles.append(potfile) else: - print 'WARNING: %s file not generated' % potfile - print '******** merging .pot files' + print '-> WARNING: %s file was not generated' % potfile + print '-> merging %i .pot files' % len(potfiles) cubicwebpot = join(tempdir, 'cubicweb.pot') execute('msgcat %s > %s' % (' '.join(potfiles), cubicwebpot)) - print '******** merging main pot file with existing translations' + print '-> merging main pot file with existing translations.' chdir(I18NDIR) toedit = [] for lang in LANGS: @@ -304,11 +303,10 @@ # cleanup rm(tempdir) # instructions pour la suite - print '*' * 72 - print 'you can now edit the following files:' + print '-> regenerated CubicWeb\'s .po catalogs.' + print '\nYou can now edit the following files:' print '* ' + '\n* '.join(toedit) - print - print "then you'll have to update cubes catalogs using the i18ncube command" + print 'when you are done, run "cubicweb-ctl i18ncube yourcube".' class UpdateTemplateCatalogCommand(Command): @@ -329,39 +327,45 @@ def update_cubes_catalogs(cubes): - toedit = [] for cubedir in cubes: + toedit = [] if not isdir(cubedir): - print 'not a directory', cubedir + print '-> ignoring %s that is not a directory.' % cubedir continue try: toedit += update_cube_catalogs(cubedir) except Exception: import traceback traceback.print_exc() - print 'error while updating catalogs for', cubedir - # instructions pour la suite - print '*' * 72 - print 'you can now edit the following files:' - print '* ' + '\n* '.join(toedit) - + print '-> error while updating catalogs for cube', cubedir + else: + # instructions pour la suite + print '-> regenerated .po catalogs for cube %s.' % cubedir + print '\nYou can now edit the following files:' + print '* ' + '\n* '.join(toedit) + print ('When you are done, run "cubicweb-ctl i18ninstance ' + '" to see changes in your instances.') 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) - print '*' * 72 - print 'updating %s cube...' % cube + tempdir = tempfile.mkdtemp() + print underline_title('Updating i18n catalogs for cube %s' % cube) chdir(cubedir) - potfiles = [join('i18n', scfile) for scfile in ('entities.pot',) - if exists(join('i18n', scfile))] - print '******** extract schema messages' + if exists(join('i18n', 'entities.pot')): + warn('entities.pot is deprecated, rename file to static-messages.pot (%s)' + % join('i18n', 'entities.pot'), DeprecationWarning) + potfiles = [join('i18n', 'entities.pot')] + elif exists(join('i18n', 'static-messages.pot')): + potfiles = [join('i18n', 'static-messages.pot')] + else: + potfiles = [] + print '-> extract schema messages' schemapot = join(tempdir, 'schema.pot') potfiles.append(schemapot) # explicit close necessary else the file may not be yet flushed when @@ -369,10 +373,10 @@ schemapotstream = file(schemapot, 'w') generate_schema_pot(schemapotstream.write, cubedir) schemapotstream.close() - print '******** extract TAL messages' + print '-> extract TAL messages' tali18nfile = join(tempdir, 'tali18n.py') extract_from_tal(find('.', ('.py', '.pt'), blacklist=STD_BLACKLIST+('test',)), tali18nfile) - print '******** extract Javascript messages' + print '-> extract Javascript messages' jsfiles = [jsfile for jsfile in find('.', '.js') if basename(jsfile).startswith('cub')] if jsfiles: tmppotfile = join(tempdir, 'js.pot') @@ -381,7 +385,7 @@ # no pot file created if there are no string to translate if exists(tmppotfile): potfiles.append(tmppotfile) - print '******** create cube specific catalog' + print '-> create cube-specific catalog' tmppotfile = join(tempdir, 'generated.pot') cubefiles = find('.', '.py', blacklist=STD_BLACKLIST+('test',)) cubefiles.append(tali18nfile) @@ -390,12 +394,12 @@ if exists(tmppotfile): # doesn't exists of no translation string found potfiles.append(tmppotfile) potfile = join(tempdir, 'cube.pot') - print '******** merging .pot files' + print '-> merging %i .pot files:' % len(potfiles) execute('msgcat %s > %s' % (' '.join(potfiles), potfile)) - print '******** merging main pot file with existing translations' + print '-> merging main pot file with existing translations:' chdir('i18n') for lang in LANGS: - print '****', lang + print '-> language', lang cubepo = '%s.po' % lang if not exists(cubepo): shutil.copy(potfile, cubepo) @@ -479,7 +483,7 @@ " Please specify it using the --directory option") cubesdir = cubespath[0] if not isdir(cubesdir): - print "creating cubes directory", cubesdir + print "-> creating cubes directory", cubesdir try: mkdir(cubesdir) except OSError, err: @@ -488,19 +492,20 @@ if exists(cubedir): self.fail("%s already exists !" % (cubedir)) skeldir = join(BASEDIR, 'skeleton') + default_name = 'cubicweb-%s' % cubename.lower() if verbose: - distname = raw_input('Debian name for your cube (just type enter to use the cube name): ').strip() + distname = raw_input('Debian name for your cube ? [%s]): ' % default_name).strip() if not distname: - distname = 'cubicweb-%s' % cubename.lower() + distname = default_name elif not distname.startswith('cubicweb-'): - if confirm('do you mean cubicweb-%s ?' % distname): + if ASK.confirm('Do you mean cubicweb-%s ?' % distname): distname = 'cubicweb-' + distname else: - distname = 'cubicweb-%s' % cubename.lower() + distname = default_name longdesc = shortdesc = raw_input('Enter a short description for your cube: ') if verbose: - longdesc = raw_input('Enter a long description (or nothing if you want to reuse the short one): ') + longdesc = raw_input('Enter a long description (leave empty to reuse the short one): ') if verbose: includes = self._ask_for_dependancies() if len(includes) == 1: @@ -525,14 +530,14 @@ def _ask_for_dependancies(self): includes = [] for stdtype in ServerConfiguration.available_cubes(): - ans = raw_input("Depends on cube %s? (N/y/s(kip)/t(ype)" - % stdtype).lower().strip() - if ans == 'y': + answer = ASK.ask("Depends on cube %s? " % stdtype, + ('N','y','skip','type'), 'N') + if answer == 'y': includes.append(stdtype) - if ans == 't': - includes = get_csv(raw_input('type dependancies: ')) + if answer == 'type': + includes = splitstrip(raw_input('type dependancies: ')) break - elif ans == 's': + elif answer == 'skip': break return includes @@ -572,22 +577,25 @@ req = requests.setdefault(rql, []) time.strip() chunks = time.split() + clocktime = float(chunks[0][1:]) cputime = float(chunks[-3]) - req.append( cputime ) + req.append( (clocktime, cputime) ) except Exception, exc: sys.stderr.write('Line %s: %s (%s)\n' % (lineno, exc, line)) stat = [] for rql, times in requests.items(): - stat.append( (sum(times), len(times), rql) ) + stat.append( (sum(time[0] for time in times), + sum(time[1] for time in times), + len(times), rql) ) stat.sort() stat.reverse() - total_time = sum(time for time, occ, rql in stat)*0.01 - print 'Percentage;Cumulative Time;Occurences;Query' - for time, occ, rql in stat: - print '%.2f;%.2f;%s;%s' % (time/total_time, time, occ, rql) + total_time = sum(clocktime for clocktime, cputime, occ, rql in stat)*0.01 + print 'Percentage;Cumulative Time (clock);Cumulative Time (CPU);Occurences;Query' + for clocktime, cputime, occ, rql in stat: + print '%.2f;%.2f;%.2f;%s;%s' % (clocktime/total_time, clocktime, cputime, occ, rql) register_commands((UpdateCubicWebCatalogCommand, UpdateTemplateCatalogCommand, diff -r 9e639925ca2f -r ff6114c2c416 devtools/fake.py --- a/devtools/fake.py Tue Aug 04 11:43:03 2009 +0200 +++ b/devtools/fake.py Tue Aug 04 15:06:09 2009 +0200 @@ -49,8 +49,7 @@ _registries = { 'controllers' : [Mock(id='view'), Mock(id='login'), Mock(id='logout'), Mock(id='edit')], - 'views' : [Mock(id='primary'), Mock(id='secondary'), - Mock(id='oneline'), Mock(id='list')], + 'views' : [Mock(id='primary'), Mock(id='oneline'), Mock(id='list')], } def registry_objects(self, name, oid=None): @@ -88,12 +87,12 @@ return None def base_url(self): - """return the root url of the application""" + """return the root url of the instance""" return BASE_URL def relative_path(self, includeparams=True): """return the normalized path of the request (ie at least relative - to the application's root, but some other normalization may be needed + to the instance's root, but some other normalization may be needed so that the returned path may be used to compare to generated urls """ if self._url.startswith(BASE_URL): @@ -188,12 +187,14 @@ self.user = user or FakeUser() self.is_internal_session = False self.is_super_session = self.user.eid == -1 - self._query_data = {} + self.transaction_data = {} def execute(self, *args): pass + unsafe_execute = execute + def commit(self, *args): - self._query_data.clear() + self.transaction_data.clear() def close(self, *args): pass def system_sql(self, sql, args=None): diff -r 9e639925ca2f -r ff6114c2c416 devtools/fill.py --- a/devtools/fill.py Tue Aug 04 11:43:03 2009 +0200 +++ b/devtools/fill.py Tue Aug 04 15:06:09 2009 +0200 @@ -10,7 +10,7 @@ from random import randint, choice from copy import deepcopy -from datetime import datetime, date, timedelta +from datetime import datetime, date, time#timedelta from decimal import Decimal from yams.constraints import (SizeConstraint, StaticVocabularyConstraint, @@ -163,7 +163,7 @@ def generate_time(self, attrname, index): """generates a random time (format is ' HH:MM')""" - return timedelta(0, 11, index%60) #'11:%02d' % (index % 60) + return time(11, index%60) #'11:%02d' % (index % 60) def generate_datetime(self, attrname, index): """generates a random date (format is 'yyyy-mm-dd HH:MM')""" @@ -225,7 +225,7 @@ :param etype: the entity's type :type schema: cubicweb.schema.Schema - :param schema: the application schema + :param schema: the instance schema :type entity_num: int :param entity_num: the number of entities to insert @@ -325,7 +325,7 @@ def make_relations_queries(schema, edict, cursor, ignored_relations=(), existingrels=None): """returns a list of generated RQL queries for relations - :param schema: The application schema + :param schema: The instance schema :param e_dict: mapping between etypes and eids diff -r 9e639925ca2f -r ff6114c2c416 devtools/migrtest.py --- a/devtools/migrtest.py Tue Aug 04 11:43:03 2009 +0200 +++ b/devtools/migrtest.py Tue Aug 04 15:06:09 2009 +0200 @@ -42,14 +42,14 @@ from logilab.common.shellutils import cp, rm from cubicweb.toolsutils import read_config -from cubicweb.server.serverctl import generate_sources_file +from cubicweb.server.utils import generate_sources_file # XXXX use db-copy instead # test environment configuration chrootpath = '/sandbox/cubicwebtest' tmpdbhost = 'crater' -tmpdbuser = 'syt' +tmpdbuser = 'syt' tmpdbpasswd = 'syt' def play_migration(applhome, applhost='', sudo=False): diff -r 9e639925ca2f -r ff6114c2c416 devtools/pkginfo.py --- a/devtools/pkginfo.py Tue Aug 04 11:43:03 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +0,0 @@ -"""distutils / __pkginfo__ helpers for cubicweb applications - -:organization: Logilab -:copyright: 2001-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 -""" - -import os -from os.path import isdir, join - - -def get_distutils_datafiles(cube, i18n=True, recursive=False): - """ - :param cube: application cube's name - """ - data_files = [] - data_files += get_basepyfiles(cube) - data_files += get_webdatafiles(cube) - if i18n: - data_files += get_i18nfiles(cube) - data_files += get_viewsfiles(cube, recursive=recursive) - data_files += get_migrationfiles(cube) - data_files += get_schemafiles(cube) - return data_files - - - -## listdir filter funcs ################################################ -def nopyc_and_nodir(fname): - if isdir(fname) or fname.endswith('.pyc') or fname.endswith('~'): - return False - return True - -def no_version_control(fname): - if fname in ('CVS', '.svn', '.hg'): - return False - if fname.endswith('~'): - return False - return True - -def basepy_files(fname): - if fname.endswith('.py') and fname != 'setup.py': - return True - return False - -def chain(*filters): - def newfilter(fname): - for filterfunc in filters: - if not filterfunc(fname): - return False - return True - return newfilter - -def listdir_with_path(path='.', filterfunc=None): - if filterfunc: - return [join(path, fname) for fname in os.listdir(path) if filterfunc(join(path, fname))] - else: - return [join(path, fname) for fname in os.listdir(path)] - - -## data_files helpers ################################################## -CUBES_DIR = join('share', 'cubicweb', 'cubes') - -def get_i18nfiles(cube): - """returns i18n files in a suitable format for distutils's - data_files parameter - """ - i18ndir = join(CUBES_DIR, cube, 'i18n') - potfiles = [(i18ndir, listdir_with_path('i18n', chain(no_version_control, nopyc_and_nodir)))] - return potfiles - - -def get_viewsfiles(cube, recursive=False): - """returns views files in a suitable format for distutils's - data_files parameter - - :param recursive: include views' subdirs recursively if True - """ - if recursive: - datafiles = [] - for dirpath, dirnames, filenames in os.walk('views'): - filenames = [join(dirpath, fname) for fname in filenames - if nopyc_and_nodir(join(dirpath, fname))] - dirpath = join(CUBES_DIR, cube, dirpath) - datafiles.append((dirpath, filenames)) - return datafiles - else: - viewsdir = join(CUBES_DIR, cube, 'views') - return [(viewsdir, - listdir_with_path('views', filterfunc=nopyc_and_nodir))] - - -def get_basepyfiles(cube): - """returns cube's base python scripts (tali18n.py, etc.) - in a suitable format for distutils's data_files parameter - """ - return [(join(CUBES_DIR, cube), - [fname for fname in os.listdir('.') - if fname.endswith('.py') and fname != 'setup.py'])] - - -def get_webdatafiles(cube): - """returns web's data files (css, png, js, etc.) in a suitable - format for distutils's data_files parameter - """ - return [(join(CUBES_DIR, cube, 'data'), - listdir_with_path('data', filterfunc=no_version_control))] - - -def get_migrationfiles(cube): - """returns cube's migration scripts - in a suitable format for distutils's data_files parameter - """ - return [(join(CUBES_DIR, cube, 'migration'), - listdir_with_path('migration', no_version_control))] - - -def get_schemafiles(cube): - """returns cube's schema files - in a suitable format for distutils's data_files parameter - """ - return [(join(CUBES_DIR, cube, 'schema'), - listdir_with_path('schema', no_version_control))] - - diff -r 9e639925ca2f -r ff6114c2c416 devtools/repotest.py --- a/devtools/repotest.py Tue Aug 04 11:43:03 2009 +0200 +++ b/devtools/repotest.py Tue Aug 04 15:06:09 2009 +0200 @@ -11,6 +11,8 @@ from pprint import pprint +from logilab.common.decorators import clear_cache + def tuplify(list): for i in range(len(list)): if type(list[i]) is not type(()): @@ -116,6 +118,9 @@ ExecutionPlan._check_permissions = _orig_check_permissions rqlannotation._select_principal = _orig_select_principal + def set_debug(self, debug): + set_debug(debug) + def _prepare(self, rql): #print '******************** prepare', rql union = self.rqlhelper.parse(rql) @@ -203,6 +208,34 @@ class BasePlannerTC(BaseQuerierTC): + newsources = 0 + def setup(self): + clear_cache(self.repo, 'rel_type_sources') + clear_cache(self.repo, 'rel_type_sources') + clear_cache(self.repo, 'can_cross_relation') + clear_cache(self.repo, 'is_multi_sources_relation') + # XXX source_defs + self.o = self.repo.querier + self.session = self.repo._sessions.values()[0] + self.pool = self.session.set_pool() + self.schema = self.o.schema + self.sources = self.o._repo.sources + self.system = self.sources[-1] + do_monkey_patch() + + def add_source(self, sourcecls, uri): + self.sources.append(sourcecls(self.repo, self.o.schema, + {'uri': uri})) + self.repo.sources_by_uri[uri] = self.sources[-1] + setattr(self, uri, self.sources[-1]) + self.newsources += 1 + + def tearDown(self): + while self.newsources: + source = self.sources.pop(-1) + del self.repo.sources_by_uri[source.uri] + self.newsources -= 1 + undo_monkey_patch() def _prepare_plan(self, rql, kwargs=None): rqlst = self.o.parse(rql, annotate=True) diff -r 9e639925ca2f -r ff6114c2c416 devtools/stresstester.py --- a/devtools/stresstester.py Tue Aug 04 11:43:03 2009 +0200 +++ b/devtools/stresstester.py Tue Aug 04 15:06:09 2009 +0200 @@ -1,4 +1,4 @@ -""" Usage: %s [OPTIONS] +""" Usage: %s [OPTIONS] Stress test a CubicWeb repository @@ -151,8 +151,8 @@ user = raw_input('login: ') if password is None: password = getpass('password: ') - from cubicweb.cwconfig import application_configuration - config = application_configuration(args[0]) + from cubicweb.cwconfig import instance_configuration + config = instance_configuration(args[0]) # get local access to the repository print "Creating repo", prof_file repo = Repository(config, prof_file) diff -r 9e639925ca2f -r ff6114c2c416 devtools/test/data/bootstrap_cubes --- a/devtools/test/data/bootstrap_cubes Tue Aug 04 11:43:03 2009 +0200 +++ b/devtools/test/data/bootstrap_cubes Tue Aug 04 15:06:09 2009 +0200 @@ -1,1 +1,1 @@ -person, comment +person diff -r 9e639925ca2f -r ff6114c2c416 devtools/test/data/schema.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devtools/test/data/schema.py Tue Aug 04 15:06:09 2009 +0200 @@ -0,0 +1,20 @@ +""" + +:organization: Logilab +:copyright: 2001-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 yams.buildobjs import EntityType, SubjectRelation, String, Int, Date + +from cubes.person.schema import Person + +Person.add_relation(Date(), 'birthday') + +class Bug(EntityType): + title = String(maxsize=64, required=True, fulltextindexed=True) + severity = String(vocabulary=('important', 'normal', 'minor'), default='normal') + cost = Int() + description = String(maxsize=4096, fulltextindexed=True) + identical_to = SubjectRelation('Bug', symetric=True) + diff -r 9e639925ca2f -r ff6114c2c416 devtools/test/data/schema/Bug.sql --- a/devtools/test/data/schema/Bug.sql Tue Aug 04 11:43:03 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -title ivarchar(64) not null -state CHOICE('open', 'rejected', 'validation pending', 'resolved') default 'open' -severity CHOICE('important', 'normal', 'minor') default 'normal' -cost integer -description ivarchar(4096) diff -r 9e639925ca2f -r ff6114c2c416 devtools/test/data/schema/Project.sql --- a/devtools/test/data/schema/Project.sql Tue Aug 04 11:43:03 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -name ivarchar(64) not null -summary ivarchar(128) -vcsurl varchar(256) -reporturl varchar(256) -description ivarchar(1024) -url varchar(128) diff -r 9e639925ca2f -r ff6114c2c416 devtools/test/data/schema/Story.sql --- a/devtools/test/data/schema/Story.sql Tue Aug 04 11:43:03 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -title ivarchar(64) not null -state CHOICE('open', 'rejected', 'validation pending', 'resolved') default 'open' -priority CHOICE('minor', 'normal', 'important') default 'normal' -cost integer -description ivarchar(4096) diff -r 9e639925ca2f -r ff6114c2c416 devtools/test/data/schema/Version.sql --- a/devtools/test/data/schema/Version.sql Tue Aug 04 11:43:03 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -num varchar(16) not null -diem date -status CHOICE('planned', 'dev', 'published') default 'planned' diff -r 9e639925ca2f -r ff6114c2c416 devtools/test/data/schema/custom.py --- a/devtools/test/data/schema/custom.py Tue Aug 04 11:43:03 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -""" - -:organization: Logilab -:copyright: 2001-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 -""" -Person = import_erschema('Person') -Person.add_relation(Date(), 'birthday') diff -r 9e639925ca2f -r ff6114c2c416 devtools/test/data/schema/relations.rel --- a/devtools/test/data/schema/relations.rel Tue Aug 04 11:43:03 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -Bug concerns Project inline -Story concerns Project inline - -Bug corrected_in Version inline CONSTRAINT E concerns P, X version_of P -Story done_in Version inline CONSTRAINT E concerns P, X version_of P - -Bug identical_to Bug symetric -Bug identical_to Story symetric -Story identical_to Story symetric - -Story depends_on Story -Story depends_on Bug -Bug depends_on Story -Bug depends_on Bug - -Bug see_also Bug symetric -Bug see_also Story symetric -Bug see_also Project symetric -Story see_also Story symetric -Story see_also Project symetric -Project see_also Project symetric - -Project uses Project - -Version version_of Project inline -Version todo_by CWUser - -Comment about Bug inline -Comment about Story inline -Comment about Comment inline - -CWUser interested_in Project - diff -r 9e639925ca2f -r ff6114c2c416 devtools/testlib.py --- a/devtools/testlib.py Tue Aug 04 11:43:03 2009 +0200 +++ b/devtools/testlib.py Tue Aug 04 15:06:09 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') @@ -249,6 +249,8 @@ def iter_automatic_rsets(self, limit=10): """generates basic resultsets for each entity type""" etypes = self.to_test_etypes() + if not etypes: + return for etype in etypes: yield self.execute('Any X LIMIT %s WHERE X is %s' % (limit, etype)) etype1 = etypes.pop() @@ -387,7 +389,7 @@ vreg._selected = {} orig_select_best = vreg.__class__.select_best def instr_select_best(self, *args, **kwargs): - selected = orig_select(self, *args, **kwargs) + selected = orig_select_best(self, *args, **kwargs) try: self._selected[selected.__class__] += 1 except KeyError: diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/.templates/layout.html --- a/doc/book/en/.templates/layout.html Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/.templates/layout.html Tue Aug 04 15:06:09 2009 +0200 @@ -4,7 +4,7 @@ {%- endblock %} {%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} {%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} -{%- macro relbar %} +{%- macro relbar() %} {%- endmacro %} -{%- macro sidebar %} +{%- macro sidebar() %} {%- if builder != 'htmlhelp' %}
diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/B000-development.en.txt --- a/doc/book/en/B000-development.en.txt Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/B000-development.en.txt Tue Aug 04 15:06:09 2009 +0200 @@ -1,12 +1,10 @@ .. -*- coding: utf-8 -*- -.. _Part2: - ===================== Part II - Development ===================== -This part is about developing web applications with the `CubicWeb` framework. +This part is about developing web applications with the *CubicWeb* framework. .. toctree:: :maxdepth: 1 diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/B0015-define-permissions.en.txt --- a/doc/book/en/B0015-define-permissions.en.txt Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/B0015-define-permissions.en.txt Tue Aug 04 15:06:09 2009 +0200 @@ -11,7 +11,7 @@ * permissions (read, update, create, delete) * permissions are assigned to groups (and not to users) -For `CubicWeb` in particular: +For *CubicWeb* in particular: * we associate rights at the enttities/relations schema level * for each entity, we distinguish four kind of permissions: read, diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/MERGE_ME-tut-create-gae-app.en.txt --- a/doc/book/en/MERGE_ME-tut-create-gae-app.en.txt Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/MERGE_ME-tut-create-gae-app.en.txt Tue Aug 04 15:06:09 2009 +0200 @@ -7,7 +7,7 @@ Ce tutoriel va vous guider pas à pas a construire une apllication web de gestion de Blog afin de vous faire découvrir les fonctionnalités de -`CubicWeb`. +*CubicWeb*. Nous supposons que vous avec déjà suivi le guide :ref:`installationGAE`. @@ -23,7 +23,7 @@ cubicweb-ctl newgapp blogdemo -`newgapp` est la commande permettant de créer une instance `CubicWeb` pour +`newgapp` est la commande permettant de créer une instance *CubicWeb* pour le datastore. Assurez-vous que votre variable d'environnement ``PYTHONPATH`` est correctement @@ -32,7 +32,7 @@ Définissez un schéma -------------------- -Le modèle de données ou schéma est au coeur d'une application `CubicWeb`. +Le modèle de données ou schéma est au coeur d'une application *CubicWeb*. C'est là où vous allez devoir définir le type de contenu que votre application devra gérer. @@ -180,7 +180,7 @@ :alt: displaying the detailed view of a blogentry Rappelez-vous que pour le moment, tout a été géré par la plate-forme -`CubicWeb` et que la seule chose qui a été fournie est le schéma de +*CubicWeb* et que la seule chose qui a été fournie est le schéma de données. D'ailleurs pour obtenir une vue graphique du schéma, exécutez la commande ``laxctl genschema blogdemo`` et vous pourrez visualiser votre schéma a l'URL suivante : http://localhost:8080/schema diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/Z010-beginners.en.txt --- a/doc/book/en/Z010-beginners.en.txt Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/Z010-beginners.en.txt Tue Aug 04 15:06:09 2009 +0200 @@ -2,7 +2,7 @@ .. _QuickInstall: -Quick Installation of a `CubicWeb` instance +Quick Installation of a *CubicWeb* instance =========================================== .. include:: C010-setup.en.txt diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/Z012-create-instance.en.txt --- a/doc/book/en/Z012-create-instance.en.txt Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/Z012-create-instance.en.txt Tue Aug 04 15:06:09 2009 +0200 @@ -7,15 +7,15 @@ What is an instance? -------------------- -A `CubicWeb` instance is a container that -refers to cubes and configuration parameters for your web application. +A *CubicWeb* instance is a container that +refers to cubes and configuration parameters for your web instance. Each instance is stored as a directory in ``~/etc/cubicweb.d`` which enables -us to run your application. +us to run your instance. What is a cube? --------------- -Cubes represent data and basic building bricks of your web applications : +Cubes represent data and basic building bricks of your web instances : blogs, person, date, addressbook and a lot more. .. XXX They related to each other by a 'Schema' which is also the PostGres representation. @@ -26,16 +26,16 @@ a debian package installation. For example, the 'blog' cube defines the entities blogs and blogentries. -When an `CubicWeb` instance is created, you list the cubes that you want to use. +When an *CubicWeb* instance is created, you list the cubes that you want to use. Using a cube means having the entities defined in your cube's schema available in your instance as well as their views and workflows. -Creating a basic `CubicWeb` Instance +Creating a basic *CubicWeb* Instance ------------------------------------ We can create an instance to view our -application in a web browser. :: +instance in a web browser. :: cubicweb-ctl create blog myblog @@ -52,17 +52,17 @@ sufficient. You can allways modify the parameters later by editing configuration files. When a user/psswd is requested to access the database please use the login you create at the time you configured the database -(:ref:`ConfigurationPostgres`). +(:ref:`ConfigurationPostgresql`). It is important to distinguish here the user used to access the database and -the user used to login to the cubicweb application. When a `CubicWeb` application +the user used to login to the cubicweb instance. When a *CubicWeb* instance starts, it uses the login/psswd for the database to get the schema and handle low level transaction. But, when ``cubicweb-ctl create`` asks for -a manager login/psswd of `CubicWeb`, it refers to an application user -to administrate your web application. +a manager login/psswd of *CubicWeb*, it refers to an instance user +to administrate your web instance. The configuration files are stored in *~/etc/cubicweb.d/myblog/*. -To launch the web application, you just type :: +To launch the web instance, you just type :: cubicweb-ctl start myblog diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/admin/create-instance.rst --- a/doc/book/en/admin/create-instance.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/admin/create-instance.rst Tue Aug 04 15:06:09 2009 +0200 @@ -6,9 +6,8 @@ Instance creation ----------------- -Now that we created our cube, we can create an instance to view our -application in a web browser. To do so we will use a `all-in-one` -configuration to simplify things :: +Now that we created a cube, we can create an instance and access it via a web +browser. We will use a `all-in-one` configuration to simplify things :: cubicweb-ctl create -c all-in-one mycube myinstance @@ -21,15 +20,15 @@ sufficient. You can anyway modify the configuration later on by editing configuration files. When a user/psswd is requested to access the database please use the login you create at the time you configured the database -(:ref:`ConfigurationPostgres`). +(:ref:`ConfigurationPostgresql`). It is important to distinguish here the user used to access the database and the -user used to login to the cubicweb application. When an instance starts, it uses +user used to login to the cubicweb instance. When an instance starts, it uses the login/psswd for the database to get the schema and handle low level transaction. But, when :command:`cubicweb-ctl create` asks for a manager -login/psswd of `CubicWeb`, it refers to the user you will use during the -development to administrate your web application. It will be possible, later on, -to use this user to create others users for your final web application. +login/psswd of *CubicWeb*, it refers to the user you will use during the +development to administrate your web instance. It will be possible, later on, +to use this user to create others users for your final web instance. Instance administration @@ -45,8 +44,8 @@ The option `-D` specify the *debug mode* : the instance is not running in server mode and does not disconnect from the termnial, which simplifies debugging in case the instance is not properly launched. You can see how it looks by -visiting the URL `http://localhost:8080` (the port number depends of your -configuration). To login, please use the cubicweb administrator login/psswd you +visiting the URL `http://localhost:8080` (the port number depends of your +configuration). To login, please use the cubicweb administrator login/psswd you defined when you created the instance. To shutdown the instance, Crtl-C in the terminal window is enough. @@ -59,5 +58,10 @@ upgrade ~~~~~~~ -XXX feed me + +The command is:: + cubicweb-ctl upgrade myinstance + +XXX write me + diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/admin/gae.rst --- a/doc/book/en/admin/gae.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/admin/gae.rst Tue Aug 04 15:06:09 2009 +0200 @@ -1,5 +1,7 @@ .. -*- coding: utf-8 -*- +.. _GoogleAppEngineSource: + CubicWeb in Google AppEngine ============================ @@ -9,9 +11,9 @@ `Google AppEngine`_ is provided with a partial port of the `Django` framework, but Google stated at Google IO 2008 that it would not support a specific Python web framework and that all -community-supported frameworks would be more than welcome [1]_. +community-supported frameworks would be more than welcome [1]_. -Therefore `Logilab`_ ported `CubicWeb` to run on top of `Google AppEngine`'s +Therefore `Logilab`_ ported *CubicWeb* to run on top of `Google AppEngine`'s datastore. .. _`Google AppEngine`: http://code.google.com/appengine/docs/whatisgoogleappengine.html @@ -25,8 +27,8 @@ http://code.google.com/appengine/downloads.html -Please follow instructions on how to install `CubicWeb` framework -(:ref:`CubicWebInstallation`). +Please follow instructions on how to install *CubicWeb* framework +(:ref:`SetUpEnv`). Installation ------------ @@ -37,7 +39,7 @@ cubicweb-ctl newgapp This will create a directory containing :: - + `-- myapp/ |-- app.conf |-- app.yaml @@ -73,17 +75,17 @@ |-- yams/ `-- yapps/ - + This skeleton directory is a working `AppEngine` application. You will recognize the files ``app.yaml`` and ``main.py``. All the rest is the -`CubicWeb` framework and its third-party libraries. You will notice that +*CubicWeb* framework and its third-party libraries. You will notice that the directory ``cubes`` is a library of reusable cubes. The main directories that you should know about are: - - ``cubes`` : this is a library of reusable yams cubes. To use - those cubes you will list them in the variable - `included-yams-cubes` of ``app.conf``. See also :ref:`cubes`. + - ``cubes`` : this is a library of reusable yams cubes. To use + those cubes you will list them in the variable + `included-yams-cubes` of ``app.conf``. See also :ref:`cubes`. - [WHICH OTHER ONES SHOULD BE LISTED HERE?] Dependencies @@ -91,7 +93,7 @@ Before starting anything, please make sure the following packages are installed: - yaml : by default google appengine is providing yaml; make sure you can - import it. We recommend you create a symbolic link yaml instead of installing + import it. We recommend you create a symbolic link yaml instead of installing and using python-yaml: yaml -> full/path/to/google_appengine/lib/yaml/lib/yaml/ - gettext @@ -99,13 +101,13 @@ Setup ~~~~~ -Once you executed ``cubicweb-ctl newgapp ``, you can use that ``myapp/`` +Once you executed ``cubicweb-ctl newgapp ``, you can use that ``myapp/`` as an application directory and do as follows. -This installation directory provides a configuration for an instance of `CubicWeb` -ported for Google App Engine. It is installed with its own command ``laxctl`` -which is a port of the command tool ``cubicweb-ctl`` originally developped for -`CubicWeb`. +This installation directory provides a configuration for an instance of *CubicWeb* +ported for Google App Engine. It is installed with its own command ``laxctl`` +which is a port of the command tool ``cubicweb-ctl`` originally developped for +*CubicWeb*. You can have the details of available commands by running :: @@ -115,25 +117,25 @@ Generating translation files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -`CubicWeb` is fully internationalized. Translation catalogs are found in +*CubicWeb* is fully internationalized. Translation catalogs are found in ``myapp/i18n``. To compile the translation files, use the `gettext` tools or the ``laxctl`` command :: - $ python myapp/bin/laxctl i18ncube + $ python myapp/bin/laxctl i18ncube $ python myapp/bin/laxctl i18ninstance Ignore the errors that print "No translation file found for domain 'cubicweb'". They disappear after the first run of i18ninstance. .. note:: The command myapp/bin/laxctl i18ncube needs to be executed - only if your application is using cubes from cubicweb-apps. + only if your instance is using cubes from cubicweb-apps. Otherwise, please skip it. -You will never need to add new entries in the translation catalog. Instead we would -recommand you to use ``self.req._("msgId")`` in your application code -to flag new message id to add to the catalog, where ``_`` refers to -xgettext that is used to collect new strings to translate. -While running ``laxctl i18ncube``, new string will be added to the catalogs. +You will never need to add new entries in the translation catalog. Instead we +would recommand you to use ``self.req._("msgId")`` in your code to flag new +message id to add to the catalog, where ``_`` refers to xgettext that is used to +collect new strings to translate. While running ``laxctl i18ncube``, new string +will be added to the catalogs. Generating the data directory ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -152,20 +154,20 @@ $ python myapp/bin/laxctl genschema -Application configuration +Instance configuration ------------------------- Authentication ~~~~~~~~~~~~~~ -You have the option of using or not google authentication for your application. +You have the option of using or not google authentication for your instance. This has to be define in ``app.conf`` and ``app.yaml``. In ``app.conf`` modify the following variable::   - # does this application rely on google authentication service or not. + # does this instance rely on google authentication service or not. use-google-auth=no - + In ``app.yaml`` comment the `login: required` set by default in the handler:: - url: .* @@ -177,7 +179,7 @@ -Quickstart : launch the application +Quickstart : launch the instance ----------------------------------- On Mac OS X platforms, drag that directory on the @@ -187,9 +189,9 @@ $ python /path/to/google_appengine/dev_appserver.py /path/to/myapp/ -Once the local server is started, visit `http://MYAPP_URL/_load `_ and sign in as administrator. -This will initialize the repository and enable you to log in into -the application and continue the installation. +Once the local server is started, visit `http://MYAPP_URL/_load `_ and sign in as administrator. +This will initialize the repository and enable you to log in into +the instance and continue the installation. You should be redirected to a page displaying a message `content initialized`. @@ -197,7 +199,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~ You, then, want to visit `http://MYAPP_URL/?vid=authinfo `_ . -If you selected not to use google authentication, you will be prompted to a +If you selected not to use google authentication, you will be prompted to a login form where you should initialize the administrator login (recommended to use admin/admin at first). You will then be redirected to a page providing you the value to provide to ``./bin/laxctl --cookie``. @@ -212,21 +214,21 @@ :alt: displaying the detailed view of the cookie values returned -.. note:: In case you are not redirected to a page providing the - option --cookie value, please visit one more time +.. note:: In case you are not redirected to a page providing the + option --cookie value, please visit one more time `http://MYAPP_URL/?vid=authinfo `_ . Once, you have this value, then return to the shell and execute :: - + $ python myapp/bin/laxctl db-init --cookie='dev_appserver_login=test@example.com:True; __session=7bbe973a6705bc5773a640f8cf4326cc' localhost:8080 .. note:: In the case you are not using google authentication, the value returned - by `http://MYAPP_URL/?vid=authinfo `_ + by `http://MYAPP_URL/?vid=authinfo `_ will look like : --cookie='__session=2b45d1a9c36c03d2a30cedb04bc37b6d' Log out by clicking in the menu at the top right corner -and restart browsing from `http://MYAPP_URL/ `_ +and restart browsing from `http://MYAPP_URL/ `_ as a normal user. Unless you did something to change it, http://MYAPP_URL should be diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/admin/index.rst --- a/doc/book/en/admin/index.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/admin/index.rst Tue Aug 04 15:06:09 2009 +0200 @@ -6,8 +6,8 @@ Part III - Administration ------------------------- -This part is for installation and administration of the `CubicWeb` framework and -applications based on that framework. +This part is for installation and administration of the *CubicWeb* framework and +instances based on that framework. .. toctree:: :maxdepth: 1 @@ -25,11 +25,11 @@ RQL logs -------- -You can configure the `CubicWeb` application to keep a log +You can configure the *CubicWeb* instance to keep a log of the queries executed against your database. To do so, -edit the configuration file of your application +edit the configuration file of your instance ``.../etc/cubicweb.d/myapp/all-in-one.conf`` and uncomment the variable ``query-log-file``:: - # web application query log file + # web instance query log file query-log-file=/tmp/rql-myapp.log diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/admin/instance-config.rst --- a/doc/book/en/admin/instance-config.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/admin/instance-config.rst Tue Aug 04 15:06:09 2009 +0200 @@ -6,11 +6,11 @@ While creating an instance, a configuration file is generated in:: - $ (CW_REGISTRY) / / .conf + $ (CW_INSTANCES_DIR) / / .conf For example:: - /etc/cubicweb.d/JPL/all-in-one.conf + /etc/cubicweb.d/myblog/all-in-one.conf It is a simple text file format INI. In the following description, each option name is prefixed with its own section and followed by its @@ -22,7 +22,7 @@ :`web.auth-model` [cookie]: authentication mode, cookie or http :`web.realm`: - realm of the application in http authentication mode + realm of the instance in http authentication mode :`web.http-session-time` [0]: period of inactivity of an HTTP session before it closes automatically. Duration in seconds, 0 meaning no expiration (or more exactly at the @@ -51,7 +51,7 @@ RewriteCond %(REQUEST_URI) ^/demo RewriteRule ^/demo$ /demo/ RewriteRule ^/demo/(.*) http://127.0.0.1:8080/$1 [L,P] - + and for the https::: RewriteCond %(REQUEST_URI) ^/ demo @@ -70,7 +70,7 @@ regular expression matching sites which could be "embedded" in the site (controllers 'embed') :`web.submit-url`: - url where the bugs encountered in the application can be mailed to + url where the bugs encountered in the instance can be mailed to RQL server configuration @@ -92,7 +92,7 @@ ----------------------------------- Web server side: -:`pyro-client.pyro-application-id`: +:`pyro-client.pyro-instance-id`: pyro identifier of RQL server (e.g. the instance name) RQL server side: @@ -107,7 +107,7 @@ hostname hosting pyro server name. If no value is specified, it is located by a request from broadcast :`pyro-name-server.pyro-ns-group` [cubicweb]: - pyro group in which to save the application + pyro group in which to save the instance Configuring e-mail @@ -125,9 +125,9 @@ :`email.smtp-port [25]`: SMTP server port to use for outgoing mail :`email.sender-name`: - name to use for outgoing mail of the application + name to use for outgoing mail of the instance :`email.sender-addr`: - address for outgoing mail of the application + address for outgoing mail of the instance :`email.default dest-addrs`: destination addresses by default, if used by the configuration of the dissemination of the model (separated by commas) diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/admin/multisources.rst --- a/doc/book/en/admin/multisources.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/admin/multisources.rst Tue Aug 04 15:06:09 2009 +0200 @@ -1,4 +1,6 @@ -Integrating some data from another instance -=========================================== +Multiple sources of data +======================== -XXX feed me \ No newline at end of file +Data sources include SQL, LDAP, RQL, mercurial and subversion. + +XXX feed me diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/admin/setup.rst --- a/doc/book/en/admin/setup.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/admin/setup.rst Tue Aug 04 15:06:09 2009 +0200 @@ -3,13 +3,13 @@ .. _SetUpEnv: =================================================== -Installation and set-up of a `CubicWeb` environment +Installation and set-up of a *CubicWeb* environment =================================================== Installation of `Cubicweb` and its dependencies ----------------------------------------------- -`CubicWeb` is packaged for Debian and Ubuntu, but can be installed from source +*CubicWeb* is packaged for Debian and Ubuntu, but can be installed from source using a tarball or the Mercurial version control system. .. _DebianInstallation: @@ -35,17 +35,24 @@ You can now install the required packages with the following command:: - apt-get update + apt-get update apt-get install cubicweb cubicweb-dev `cubicweb` installs the framework itself, allowing you to create -new applications. +new instances. `cubicweb-dev` installs the development environment allowing you to develop new cubes. There is also a wide variety of cubes listed on http://www.cubicweb.org/Project available as debian packages and tarball. +The repositories are signed with `Logilab's gnupg key`_. To avoid warning on "apt-get update": +1. become root using sudo +2. download http://ftp.logilab.org/dists/logilab-dists-key.asc using e.g. wget +3. run "apt-key add logilab-dists-key.asc" +4. re-run apt-get update (manually or through the package manager, whichever you prefer) + +.. _`Logilab's gnupg key`: http://ftp.logilab.org/dists/logilab-dists-key.asc Install from source ``````````````````` @@ -62,19 +69,29 @@ 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. -Postgres installation -````````````````````` +In both cases, make sure you have installed the dependencies (see appendixes for +the list). -Please refer to the `Postgresql project online documentation`_. +PostgreSQL installation +``````````````````````` + +Please refer to the `PostgreSQL project online documentation`_. -.. _`Postgresql project online documentation`: http://www.postgresql.org/ +.. _`PostgreSQL project online documentation`: http://www.postgresql.org/ -You need to install the three following packages: `postgres-8.3`, -`postgres-contrib-8.3` and `postgresql-plpython-8.3`. +You need to install the three following packages: `postgresql-8.3`, +`postgresql-contrib-8.3` and `postgresql-plpython-8.3`. -Then you can install: +Other dependencies +`````````````````` + +You can also install: * `pyro` if you wish the repository to be accessible through Pyro or if the client and the server are not running on the same machine @@ -88,26 +105,27 @@ Environment configuration ------------------------- -If you installed `CubicWeb` by cloning the Mercurial forest, then you -will need to update the environment variable PYTHONPATH by adding +If you installed *CubicWeb* by cloning the Mercurial forest, then you +will need to update the environment variable PYTHONPATH by adding the path to the forest ``cubicweb``: 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 instances +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 @@ -119,54 +137,54 @@ -.. _ConfigurationPostgres: +.. _ConfigurationPostgresql: -Postgres configuration -`````````````````````` +PostgreSQL configuration +```````````````````````` .. note:: - If you already have an existing cluster and postgres server + If you already have an existing cluster and PostgreSQL server running, you do not need to execute the initilization step - of your Postgres database. + of your PostgreSQL database. -* First, initialize the database Postgres with the command ``initdb``. +* First, initialize the database PostgreSQL with the command ``initdb``. :: $ initdb -D /path/to/pgsql - Once initialized, start the database server Postgres + Once initialized, start the database server PostgreSQL with the command:: - + $ postgres -D /path/to/psql If you cannot execute this command due to permission issues, please make sure that your username has write access on the database. :: - + $ chown username /path/to/pgsql * The database authentication can be either set to `ident sameuser` - or `md5`. + or `md5`. If set to `md5`, make sure to use an existing user of your database. If set to `ident sameuser`, make sure that your client's operating system user name has a matching user in the database. If not, please do as follow to create a user:: - + $ su $ su - postgres $ createuser -s -P username The option `-P` (for password prompt), will encrypt the password with - the method set in the configuration file ``pg_hba.conf``. + the method set in the configuration file ``pg_hba.conf``. If you do not use this option `-P`, then the default value will be null and you will need to set it with:: - + $ su postgres -c "echo ALTER USER username WITH PASSWORD 'userpasswd' | psql" This login/password will be requested when you will create an instance with `cubicweb-ctl create` to initialize the database of - your application. + your instance. .. note:: The authentication method can be configured in ``pg_hba.conf``. @@ -184,13 +202,17 @@ MySql configuration ``````````````````` -Yout must add the following lines in /etc/mysql/my.cnf file:: +Yout must add the following lines in ``/etc/mysql/my.cnf`` file:: transaction-isolation = READ-COMMITTED default-storage-engine=INNODB default-character-set=utf8 max_allowed_packet = 128M +.. note:: + It is unclear whether mysql supports indexed string of arbitrary lenght or + not. + Pyro configuration ------------------ diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/admin/site-config.rst --- a/doc/book/en/admin/site-config.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/admin/site-config.rst Tue Aug 04 15:06:09 2009 +0200 @@ -5,7 +5,7 @@ .. image:: ../images/lax-book.03-site-config-panel.en.png -This panel allows you to configure the appearance of your application site. +This panel allows you to configure the appearance of your instance site. Six menus are available and we will go through each of them to explain how to use them. @@ -14,12 +14,12 @@ This menu provides you a way to adjust some navigation options depending on your needs, such as the number of entities to display by page of results. Follows the detailled list of available options : - + * navigation.combobox-limit : maximum number of entities to display in related combo box (sample format: 23) -* navigation.page-size : maximum number of objects displayed by page of results +* navigation.page-size : maximum number of objects displayed by page of results (sample format: 23) -* navigation.related-limit : maximum number of related entities to display in +* navigation.related-limit : maximum number of related entities to display in the primary view (sample format: 23) * navigation.short-line-size : maximum number of characters in short description (sample format: 23) @@ -47,17 +47,17 @@ Actions ~~~~~~~ This menu provides a way to configure the context in which you expect the actions -to be displayed to the user and if you want the action to be visible or not. -You must have notice that when you view a list of entities, an action box is -available on the left column which display some actions as well as a drop-down -menu for more actions. +to be displayed to the user and if you want the action to be visible or not. +You must have notice that when you view a list of entities, an action box is +available on the left column which display some actions as well as a drop-down +menu for more actions. The context available are : * mainactions : actions listed in the left box * moreactions : actions listed in the `more` menu of the left box * addrelated : add actions listed in the left box -* useractions : actions listed in the first section of drop-down menu +* useractions : actions listed in the first section of drop-down menu accessible from the right corner user login link * siteactions : actions listed in the second section of drop-down menu accessible from the right corner user login link @@ -65,15 +65,15 @@ Boxes ~~~~~ -The application has already a pre-defined set of boxes you can use right away. +The instance has already a pre-defined set of boxes you can use right away. This configuration section allows you to place those boxes where you want in the -application interface to customize it. +instance interface to customize it. The available boxes are : * actions box : box listing the applicable actions on the displayed data -* boxes_blog_archives_box : box listing the blog archives +* boxes_blog_archives_box : box listing the blog archives * possible views box : box listing the possible views for the displayed data @@ -81,8 +81,8 @@ * search box : search box -* startup views box : box listing the configuration options available for - the application site, such as `Preferences` and `Site Configuration` +* startup views box : box listing the configuration options available for + the instance site, such as `Preferences` and `Site Configuration` Components ~~~~~~~~~~ diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/annexes/cookbook.rst --- a/doc/book/en/annexes/cookbook.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/annexes/cookbook.rst Tue Aug 04 15:06:09 2009 +0200 @@ -7,11 +7,14 @@ life easier. -* How to import LDAP users in `CubicWeb`? +* How to import LDAP users in *CubicWeb*? + + [XXX distribute this script with cubicweb instead] Here is a very useful script which enables you to import LDAP users - into your `CubicWeb` application by running the following: :: + into your *CubicWeb* instance by running the following: +.. sourcecode:: python import os import pwd @@ -66,15 +69,17 @@ * How to load data from a script? The following script aims at loading data within a script assuming pyro-nsd is - running and your application is configured with ``pyro-server=yes``, otherwise - you would not be able to use dbapi. :: + running and your instance is configured with ``pyro-server=yes``, otherwise + you would not be able to use dbapi. + +.. sourcecode:: python from cubicweb import dbapi - + cnx = dbapi.connection(database='instance-id', user='admin', password='admin') cur = cnx.cursor() for name in ('Personal', 'Professional', 'Computers'): cur.execute('INSERT Blog B: B name %s', name) cnx.commit() - + diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/annexes/cubicweb-ctl.rst --- a/doc/book/en/annexes/cubicweb-ctl.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/annexes/cubicweb-ctl.rst Tue Aug 04 15:06:09 2009 +0200 @@ -5,7 +5,7 @@ ``cubicweb-ctl`` tool ===================== -`cubicweb-ctl` is the swiss knife to manage `CubicWeb` instances. +`cubicweb-ctl` is the swiss knife to manage *CubicWeb* instances. The general syntax is :: cubicweb-ctl [options command] @@ -15,7 +15,7 @@ cubicweb-ctl cubicweb-ctl --help -Please note that the commands available depends on the `CubicWeb` packages +Please note that the commands available depends on the *CubicWeb* packages and cubes that have been installed. To view the help menu on specific command :: @@ -26,9 +26,9 @@ ------------------------ * ``newcube``, create a new cube on the file system based on the name - given in the parameters. This command create a cube from an application - skeleton that includes default files required for debian packaging. - + given in the parameters. This command create a cube from a skeleton + that includes default files required for debian packaging. + Command to create an instance ----------------------------- @@ -62,20 +62,20 @@ Commands to maintain instances ------------------------------ * ``upgrade``, launches the existing instances migration when a new version - of `CubicWeb` or the cubes installed is available + of *CubicWeb* or the cubes installed is available * ``shell``, opens a migration shell for manual maintenance of the instance * ``db-dump``, creates a dump of the system database * ``db-restore``, restores a dump of the system database * ``db-check``, checks data integrity of an instance. If the automatic correction is activated, it is recommanded to create a dump before this operation. * ``schema-sync``, synchronizes the persistent schema of an instance with - the application schema. It is recommanded to create a dump before this operation. + the instance schema. It is recommanded to create a dump before this operation. Commands to maintain i18n catalogs ---------------------------------- -* ``i18ncubicweb``, regenerates messages catalogs of the `CubicWeb` library +* ``i18ncubicweb``, regenerates messages catalogs of the *CubicWeb* library * ``i18ncube``, regenerates the messages catalogs of a cube -* ``i18ninstance``, recompiles the messages catalogs of an instance. +* ``i18ninstance``, recompiles the messages catalogs of an instance. This is automatically done while upgrading. See also chapter :ref:`internationalisation`. @@ -116,7 +116,7 @@ This will create a new cube in ``/path/to/forest/cubicweb/cubes/`` for a Mercurial forest installation, or in ``/usr/share/cubicweb/cubes`` -for a debian packages installation, and then create an instance as +for a debian packages installation, and then create an instance as explained just above. diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/annexes/depends.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/annexes/depends.rst Tue Aug 04 15:06:09 2009 +0200 @@ -0,0 +1,51 @@ +.. -*- coding: utf-8 -*- + +.. _dependencies: + +Dependencies +============ + +When you run CubicWeb from source, either by downloading the tarball or +cloning the mercurial forest, here is the list of tools and libraries you need +to have installed in order for CubicWeb to work: + +* mxDateTime - http://www.egenix.com/products/python/mxBase/mxDateTime/ - http://pypi.python.org/pypi/egenix-mx-base + +* pyro - http://pyro.sourceforge.net/ - http://pypi.python.org/pypi/Pyro + +* yapps - http://theory.stanford.edu/~amitp/yapps/ - + http://pypi.python.org/pypi/Yapps2 + +* pygraphviz - http://networkx.lanl.gov/pygraphviz/ - + http://pypi.python.org/pypi/pygraphviz + +* simplejson - http://code.google.com/p/simplejson/ - + http://pypi.python.org/pypi/simplejson + +* lxml - http://codespeak.net/lxml - http://pypi.python.org/pypi/lxml + +* twisted - http://twistedmatrix.com/ - http://pypi.python.org/pypi/Twisted + +* logilab-common - http://www.logilab.org/project/logilab-common - + http://pypi.python.org/pypi/logilab-common/ - included in the forest + +* logilab-constraint - http://www.logilab.org/project/logilab-constraint - + http://pypi.python.org/pypi/constraint/ - included in the forest + +* logilab-mtconverter - http://www.logilab.org/project/logilab-mtconverter - + http://pypi.python.org/pypi/logilab-mtconverter - included in the forest + +* rql - http://www.logilab.org/project/rql - http://pypi.python.org/pypi/rql - + included in the forest + +* yams - http://www.logilab.org/project/yams - http://pypi.python.org/pypi/yams + - included in the forest + +* indexer - http://www.logilab.org/project/indexer - + http://pypi.python.org/pypi/indexer - included in the forest + +* fyzz - http://www.logilab.org/project/fyzz - http://pypi.python.org/pypi/fyzz + - included in the forest + +Any help with the packaging of CubicWeb for more than Debian/Ubuntu (including +eggs, buildouts, etc) will be greatly appreciated. diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/annexes/faq.rst --- a/doc/book/en/annexes/faq.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/annexes/faq.rst Tue Aug 04 15:06:09 2009 +0200 @@ -14,132 +14,137 @@ Why does not CubicWeb have a template language ? ------------------------------------------------ - There are enough template languages out there. You can use your - preferred template language if you want. [explain how to use a - template language] +There are enough template languages out there. You can use your +preferred template language if you want. [explain how to use a +template language] - `CubicWeb` does not define its own templating language as this was - not our goal. Based on our experience, we realized that - we could gain productivity by letting designers use design tools - and developpers develop without the use of the templating language - as an intermediary that could not be anyway efficient for both parties. - Python is the templating language that we use in `CubicWeb`, but again, - it does not prevent you from using a templating language. +*CubicWeb* does not define its own templating language as this was +not our goal. Based on our experience, we realized that +we could gain productivity by letting designers use design tools +and developpers develop without the use of the templating language +as an intermediary that could not be anyway efficient for both parties. +Python is the templating language that we use in *CubicWeb*, but again, +it does not prevent you from using a templating language. - The reason template languages are not used in this book is that - experience has proved us that using pure python was less cumbersome. +The reason template languages are not used in this book is that +experience has proved us that using pure python was less cumbersome. Why do you think using pure python is better than using a template language ? ----------------------------------------------------------------------------- - Python is an Object Oriented Programming language and as such it - already provides a consistent and strong architecture and syntax - a templating language would not reach. +Python is an Object Oriented Programming language and as such it +already provides a consistent and strong architecture and syntax +a templating language would not reach. - When doing development, you need a real language and template - languages are not real languages. +When doing development, you need a real language and template +languages are not real languages. - Using Python enables developing applications for which code is - easier to maintain with real functions/classes/contexts - without the need of learning a new dialect. By using Python, - we use standard OOP techniques and this is a key factor in a - robust application. +Using Python instead of a template langage for describing the user interface +makes it to maintain with real functions/classes/contexts without the need of +learning a new dialect. By using Python, we use standard OOP techniques and +this is a key factor in a robust application. Why do you use the LGPL license to prevent me from doing X ? ------------------------------------------------------------ +------------------------------------------------------------ - LGPL means that *if* you redistribute your application, you need to - redistribute the changes you made to CubicWeb under the LGPL licence. +LGPL means that *if* you redistribute your application, you need to +redistribute the changes you made to CubicWeb under the LGPL licence. - Publishing a web site has nothing to do with redistributing - source code. A fair amount of companies use modified LGPL code - for internal use. And someone could publish a `CubicWeb` component - under a BSD licence for others to plug into a LGPL framework without - any problem. The only thing we are trying to prevent here is someone - taking the framework and packaging it as closed source to his own - clients. +Publishing a web site has nothing to do with redistributing +source code. A fair amount of companies use modified LGPL code +for internal use. And someone could publish a *CubicWeb* component +under a BSD licence for others to plug into a LGPL framework without +any problem. The only thing we are trying to prevent here is someone +taking the framework and packaging it as closed source to his own +clients. CubicWeb looks pretty recent. Is it stable ? -------------------------------------------- - It is constantly evolving, piece by piece. The framework has evolved since - 2001 and data has been migrated from one schema to the other ever since. There - is a well-defined way to handle data and schema migration. +It is constantly evolving, piece by piece. The framework has evolved since +2001 and data has been migrated from one schema to the other ever since. There +is a well-defined way to handle data and schema migration. Why is the RQL query language looking similar to X ? ----------------------------------------------------- - It may remind you of SQL but it is higher level than SQL, more like - SPARQL. Except that SPARQL did not exist when we started the project. - Having SPARQL has a query language has been in our backlog for years. +It may remind you of SQL but it is higher level than SQL, more like +SPARQL. Except that SPARQL did not exist when we started the project. +With version 3.4, CubicWeb has support for SPARQL. - That RQL language is what is going to make a difference with django- - like frameworks for several reasons. +That RQL language is what is going to make a difference with django- +like frameworks for several reasons. - 1. accessing data is *much* easier with it. One can write complex - queries with RQL that would be tedious to define and hard to maintain - using an object/filter suite of method calls. +1. accessing data is *much* easier with it. One can write complex + queries with RQL that would be tedious to define and hard to maintain + using an object/filter suite of method calls. - 2. it offers an abstraction layer allowing your applications to run - on multiple back-ends. That means not only various SQL backends - (postgresql, sqlite, mysql), but also multiple databases at the - same time, and also non-SQL data stores like LDAP directories and - subversion/mercurial repositories (see the `vcsfile` - component). Google App Engine is yet another supported target for - RQL. +2. it offers an abstraction layer allowing your applications to run + on multiple back-ends. That means not only various SQL backends + (postgresql, sqlite, mysql), but also multiple databases at the + same time, and also non-SQL data stores like LDAP directories and + subversion/mercurial repositories (see the `vcsfile` + component). Google App Engine is yet another supported target for + RQL. [copy answer from forum, explain why similar to sparql and why better than django and SQL] -which ajax library ------------------- -[we use jquery and things on top of that] +which ajax library is CubicWeb using ? +-------------------------------------- + +[CubicWeb uses jQuery and adds a thin layer on top of that] -How to implement security? --------------------------- +How is security implemented ? +------------------------------ - This is an example of how it works in our framework:: +This is an example of how it works in our framework: + +.. sourcecode:: python class Version(EntityType): - """a version is defining the content of a particular project's - release""" - # definition of attributes is voluntarily missing - permissions = {'read': ('managers', 'users', 'guests',), - 'update': ('managers', 'logilab', 'owners',), - 'delete': ('managers', ), - 'add': ('managers', 'logilab', - ERQLExpression('X version_of PROJ, U in_group G, PROJ - require_permission P, P name "add_version", P require_group G'),)} + """a version is defining the content of a particular project's + release""" + # definition of attributes is voluntarily missing + permissions = {'read': ('managers', 'users', 'guests',), + 'update': ('managers', 'logilab', 'owners',), + 'delete': ('managers', ), + 'add': ('managers', 'logilab', + ERQLExpression('X version_of PROJ, U in_group G, ' + 'PROJ require_permission P, ' + 'P name "add_version", P require_group G'),)} - The above means that permission to read a Version is granted to any - user that is part of one of the groups 'managers', 'users', 'guests'. - The 'add' permission is granted to users in group 'managers' or - 'logilab' and to users in group G, if G is linked by a permission - entity named "add_version" to the version's project. - :: +The above means that permission to read a Version is granted to any +user that is part of one of the groups 'managers', 'users', 'guests'. +The 'add' permission is granted to users in group 'managers' or +'logilab' and to users in group G, if G is linked by a permission +entity named "add_version" to the version's project. + +.. sourcecode:: python class version_of(RelationType): """link a version to its project. A version is necessarily linked to one and only one project. """ # some lines voluntarily missing - permissions = {'read': ('managers', 'users', 'guests',), + permissions = {'read': ('managers', 'users', 'guests',), 'delete': ('managers', ), 'add': ('managers', 'logilab', - RRQLExpression('O require_permission P, P name "add_version", - 'U in_group G, P require_group G'),) } + RRQLExpression('O require_permission P, P name "add_version", ' + 'U in_group G, P require_group G'),) } - You can find additional information in the section :ref:`security`. +You can find additional information in the section :ref:`security`. - [XXX what does the second example means in addition to the first one?] +[XXX what does the second example means in addition to the first one?] -`Error while publishing rest text ...` --------------------------------------- - While modifying the description of an entity, you get an error message in - the application `Error while publishing ...` for Rest text and plain text. - The server returns a traceback like as follows :: +What is `Error while publishing rest text ...` ? +------------------------------------------------ + +While modifying the description of an entity, you get an error message in +the instance `Error while publishing ...` for Rest text and plain text. +The server returns a traceback like as follows :: 2008-10-06 15:05:08 - (cubicweb.rest) ERROR: error while publishing ReST text Traceback (most recent call last): @@ -148,73 +153,69 @@ file = __builtin__.open(filename, mode, buffering) TypeError: __init__() takes at most 3 arguments (4 given) +This can be fixed by applying the patch described in : +http://code.google.com/p/googleappengine/issues/detail?id=48 - This can be fixed by applying the patch described in : - http://code.google.com/p/googleappengine/issues/detail?id=48 +What are hooks used for ? +------------------------- -What are hooks used for? ------------------------- - - Hooks are executed around (actually before or after) events. The - most common events are data creation, update and deletion. They - permit additional constraint checking (those not expressible at the - schema level), pre and post computations depending on data - movements. +Hooks are executed around (actually before or after) events. The +most common events are data creation, update and deletion. They +permit additional constraint checking (those not expressible at the +schema level), pre and post computations depending on data +movements. - As such, they are a vital part of the framework. +As such, they are a vital part of the framework. - Other kinds of hooks, called Operations, are available - for execution just before commit. +Other kinds of hooks, called Operations, are available +for execution just before commit. -When should you define an HTML template rather than define a graphical component? ---------------------------------------------------------------------------------- +When should you define an HTML template rather than define a graphical component ? +---------------------------------------------------------------------------------- - An HTML template cannot contain code, hence it is only about static - content. A component is made of code and operations that apply on a - well defined context (request, result set). It enables much more - dynamic views. +An HTML template cannot contain code, hence it is only about static +content. A component is made of code and operations that apply on a +well defined context (request, result set). It enables much more +dynamic views. What is the difference between `AppRsetObject` and `AppObject` ? ---------------------------------------------------------------- - `AppRsetObject` instances are selected on a request and a result - set. `AppObject` instances are directly selected by id. +`AppRsetObject` instances are selected on a request and a result +set. `AppObject` instances are directly selected by id. -How to update a database after a schema modification? ------------------------------------------------------ - - It depends on what has been modified in the schema. +How to update a database after a schema modification ? +------------------------------------------------------ - * Update of an attribute permissions and properties: - ``synchronize_eschema('MyEntity')``. +It depends on what has been modified in the schema. - * Update of a relation permissions and properties: - ``synchronize_rschema('MyRelation')``. +* Update the permissions and properties of an entity or a relation: + ``sync_schema_props_perms('MyEntityOrRelation')``. - * Add an attribute: ``add_attribute('MyEntityType', 'myattr')``. +* Add an attribute: ``add_attribute('MyEntityType', 'myattr')``. - * Add a relation: ``add_relation_definition('SubjRelation', 'MyRelation', 'ObjRelation')``. +* Add a relation: ``add_relation_definition('SubjRelation', 'MyRelation', 'ObjRelation')``. -How to create an anonymous user? --------------------------------- +How to create an anonymous user ? +--------------------------------- - This allows to bypass authentication for your site. In the - ``all-in-one.conf`` file of your instance, define the anonymous user - as follows :: +This allows to bypass authentication for your site. In the +``all-in-one.conf`` file of your instance, define the anonymous user +as follows :: - # login of the CubicWeb user account to use for anonymous user (if you want to - # allow anonymous) - anonymous-user=anon + # login of the CubicWeb user account to use for anonymous user (if you want to + # allow anonymous) + anonymous-user=anon - # password of the CubicWeb user account matching login - anonymous-password=anon + # password of the CubicWeb user account matching login + anonymous-password=anon - You also must ensure that this `anon` user is a registered user of - the DB backend. If not, you can create through the administation - interface of your instance by adding a user with the role `guests`. - This could be the admin account (for development - purposes, of course). +You also must ensure that this `anon` user is a registered user of +the DB backend. If not, you can create through the administation +interface of your instance by adding a user with the role `guests`. +This could be the admin account (for development +purposes, of course). .. note:: While creating a new instance, you can decide to allow access @@ -222,94 +223,111 @@ decribed above. -How to change the application logo? ------------------------------------ +How to change the instance logo ? +------------------------------------ - There are two ways of changing the logo. +There are two ways of changing the logo. - 1. The easiest way to use a different logo is to replace the existing - ``logo.png`` in ``myapp/data`` by your prefered icon and refresh. - By default all application will look for a ``logo.png`` to be - rendered in the logo section. +1. The easiest way to use a different logo is to replace the existing + ``logo.png`` in ``myapp/data`` by your prefered icon and refresh. + By default all instance will look for a ``logo.png`` to be + rendered in the logo section. - .. image:: ../images/lax-book.06-main-template-logo.en.png + .. image:: ../images/lax-book.06-main-template-logo.en.png - 2. In your cube directory, you can specify which file to use for the logo. - This is configurable in ``mycube/data/external_resources``: :: +2. In your cube directory, you can specify which file to use for the logo. + This is configurable in ``mycube/data/external_resources``: :: - LOGO = DATADIR/path/to/mylogo.gif + LOGO = DATADIR/path/to/mylogo.gif - where DATADIR is ``mycubes/data``. + where DATADIR is ``mycube/data``. -How to configure LDAP source? -------------------------------- +How to configure a LDAP source ? +-------------------------------- - Your instance's sources are defined in ``/etc/cubicweb.d/myapp/sources``. - Configuring an LDAP source is about declaring that source in your - instance configuration file such as: :: +Your instance's sources are defined in ``/etc/cubicweb.d/myapp/sources``. +Configuring an LDAP source is about declaring that source in your +instance configuration file such as: :: - [ldapuser] - adapter=ldapuser - # ldap host - host=myhost - # base DN to lookup for usres - user-base-dn=ou=People,dc=mydomain,dc=fr - # user search scope - user-scope=ONELEVEL - # classes of user - user-classes=top,posixAccount - # attribute used as login on authentication - user-login-attr=uid - # name of a group in which ldap users will be by default - user-default-group=users - # map from ldap user attributes to cubicweb attributes - user-attrs-map=gecos:email,uid:login + [ldapuser] + adapter=ldapuser + # ldap host + host=myhost + # base DN to lookup for usres + user-base-dn=ou=People,dc=mydomain,dc=fr + # user search scope + user-scope=ONELEVEL + # classes of user + user-classes=top,posixAccount + # attribute used as login on authentication + user-login-attr=uid + # name of a group in which ldap users will be by default + user-default-group=users + # map from ldap user attributes to cubicweb attributes + user-attrs-map=gecos:email,uid:login - Any change applied to configuration file requires to restart your - application. +Any change applied to configuration file requires to restart your +instance. -I get NoSelectableObject exceptions: how do I debug selectors ? +I get NoSelectableObject exceptions, how do I debug selectors ? --------------------------------------------------------------- - You just need to put the appropriate context manager around view/component - selection: :: +You just need to put the appropriate context manager around view/component +selection (one standard place in in vreg.py): + +.. sourcecode:: python + + def possible_objects(self, registry, *args, **kwargs): + """return an iterator on possible objects in a registry for this result set - from cubicweb.common.selectors import traced_selection - with traced_selection(): - comp = self.vreg.select_object('contentnavigation', 'wfhistory', - self.req, rset, context='navcontentbottom') + actions returned are classes, not instances + """ + from cubicweb.selectors import traced_selection + with traced_selection(): + for vobjects in self.registry(registry).values(): + try: + yield self.select(vobjects, *args, **kwargs) + except NoSelectableObject: + continue - This will yield additional WARNINGs, like this: :: +Don't forget the 'from __future__ import with_statement' at the module +top-level. + +This will yield additional WARNINGs, like this:: 2009-01-09 16:43:52 - (cubicweb.selectors) WARNING: selector one_line_rset returned 0 for -How to format an entity date attribute? ---------------------------------------- +How to format an entity date attribute ? +---------------------------------------- - If your schema has an attribute of type Date or Datetime, you might - want to format it. First, you should define your preferred format using - the site configuration panel ``http://appurl/view?vid=systempropertiesform`` - and then set ``ui.date`` and/or ``ui.datetime``. - Then in the view code, use:: - +If your schema has an attribute of type Date or Datetime, you might +want to format it. First, you should define your preferred format using +the site configuration panel ``http://appurl/view?vid=systempropertiesform`` +and then set ``ui.date`` and/or ``ui.datetime``. +Then in the view code, use: + +.. sourcecode:: python + self.format_date(entity.date_attribute) Can PostgreSQL and CubicWeb authentication work with kerberos ? ---------------------------------------------------------------- - If you have postgresql set up to accept kerberos authentication, you can set - the db-host, db-name and db-user parameters in the `sources` configuration - file while leaving the password blank. It should be enough for your instance - to connect to postgresql with a kerberos ticket. +If you have PostgreSQL set up to accept kerberos authentication, you can set +the db-host, db-name and db-user parameters in the `sources` configuration +file while leaving the password blank. It should be enough for your +instance to connect to postgresql with a kerberos ticket. + - -How to load data from a script? -------------------------------- +How to load data from a script ? +-------------------------------- - The following script aims at loading data within a script assuming pyro-nsd is - running and your application is configured with ``pyro-server=yes``, otherwise - you would not be able to use dbapi. :: +The following script aims at loading data within a script assuming pyro-nsd is +running and your instance is configured with ``pyro-server=yes``, otherwise +you would not be able to use dbapi. + +.. sourcecode:: python from cubicweb import dbapi @@ -319,36 +337,51 @@ cur.execute('INSERT Blog B: B name %s', name) cnx.commit() -What is the CubicWeb datatype corresponding to GAE datastore's UserProperty? ----------------------------------------------------------------------------- +What is the CubicWeb datatype corresponding to GAE datastore's UserProperty ? +----------------------------------------------------------------------------- - If you take a look at your application schema and - click on "display detailed view of metadata" you will see that there - is a Euser entity in there. That's the one that is modeling users. The - thing that corresponds to a UserProperty is a relationship between - your entity and the Euser entity. As in :: +If you take a look at your instance schema and +click on "display detailed view of metadata" you will see that there +is a Euser entity in there. That's the one that is modeling users. The +thing that corresponds to a UserProperty is a relationship between +your entity and the Euser entity. As in: + +.. sourcecode:: python class TodoItem(EntityType): text = String() todo_by = SubjectRelation('Euser') - [XXX check that cw handle users better by - mapping Google Accounts to local Euser entities automatically] +[XXX check that cw handle users better by mapping Google Accounts to local Euser +entities automatically] -How to reset the password for user joe? ---------------------------------------- +How to reset the password for user joe ? +---------------------------------------- - You need to generate a new encrypted password:: +If you want to reset the admin password for ``myinstance``, do:: + + $ cubicweb-ctl reset-admin-pwd myinstance + +You need to generate a new encrypted password:: $ python >>> from cubicweb.server.utils import crypt_password >>> crypt_password('joepass') 'qHO8282QN5Utg' - >>> + >>> - and paste it in the database:: +and paste it in the database:: $ psql mydb mydb=> update cw_cwuser set cw_upassword='qHO8282QN5Utg' where cw_login='joe'; - UPDATE 1 \ No newline at end of file + 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 9e639925ca2f -r ff6114c2c416 doc/book/en/annexes/index.rst --- a/doc/book/en/annexes/index.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/annexes/index.rst Tue Aug 04 15:06:09 2009 +0200 @@ -7,7 +7,7 @@ -------------------- The following chapters are reference material. - + .. toctree:: :maxdepth: 1 @@ -16,6 +16,7 @@ cubicweb-ctl rql/index mercurial + depends (X)HTML tricks to apply ----------------------- diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/annexes/mercurial.rst --- a/doc/book/en/annexes/mercurial.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/annexes/mercurial.rst Tue Aug 04 15:06:09 2009 +0200 @@ -12,7 +12,7 @@ next, and so on). Locally, we have a repository containing revisions tree, and a working directory. It is possible to put in its working directory, one of the versions of its local repository, -modify and then push it in its repository. +modify and then push it in its repository. It is also possible to get revisions from another repository or to export its own revisions from the local repository to another repository. @@ -83,7 +83,7 @@ hg incoming ssh://myhost//home/src/repo -* See what is the revision of the local repository which has been taken out +* See what is the revision of the local repository which has been taken out from the working directory and amended:: hg parent @@ -114,8 +114,8 @@ Installation of the forest extension ```````````````````````````````````` -Set up the forest extension by getting a copy of the sources -from http://hg.akoha.org/hgforest/ and adding the following +Set up the forest extension by getting a copy of the sources +from http://hg.akoha.org/hgforest/ and adding the following lines to your ``~/.hgrc``: :: [extensions] diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/annexes/rql/implementation.rst --- a/doc/book/en/annexes/rql/implementation.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/annexes/rql/implementation.rst Tue Aug 04 15:06:09 2009 +0200 @@ -53,7 +53,7 @@ relation ::= 'NOT'? VARIABLE R_TYPE COMP_OP? expression | 'NOT'? R_TYPE VARIABLE 'IN' '(' expression (',' expression)* ')' - + expression ::= var_or_func_or_const (MATH_OP var_or_func_or_const) * | '(' expression ')' @@ -83,7 +83,7 @@ Internal representation (syntactic tree) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The tree research does not contain the selected variables +The tree research does not contain the selected variables (e.g. there is only what follows "WHERE"). The insertion tree does not contain the variables inserted or relations @@ -156,7 +156,7 @@ Document class Type <-> Document occurence_of Fiche class Type Sheet class Type <-> Form collection Collection class Type - + Therefore 1. becomes:: Document X where diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/annexes/rql/index.rst --- a/doc/book/en/annexes/rql/index.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/annexes/rql/index.rst Tue Aug 04 15:06:09 2009 +0200 @@ -8,5 +8,4 @@ intro language - dbapi implementation diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/annexes/rql/intro.rst --- a/doc/book/en/annexes/rql/intro.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/annexes/rql/intro.rst Tue Aug 04 15:06:09 2009 +0200 @@ -5,41 +5,38 @@ Goals of RQL ~~~~~~~~~~~~ -The goal is to have a language emphasizing the way of browsing -relations. As such, attributes will be regarded as cases of -special relations (in terms of implementation, the language -user should see virtually no difference between an attribute and a +The goal is to have a language emphasizing the way of browsing relations. As +such, attributes will be regarded as cases of special relations (in terms of +implementation, the user should see no difference between an attribute and a relation). -RQL is inspired by SQL but is the highest level. A knowledge of the -`CubicWeb` schema defining the application is necessary. - Comparison with existing languages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SQL ``` -RQL builds on the features of SQL but is at a higher level -(the current implementation of RQL generates SQL). For that it is limited -to the way of browsing relations and introduces variables. -The user does not need to know the model underlying SQL, but the `CubicWeb` -schema defining the application. + +RQL may remind of SQL but works at a higher abstraction level (the *CubicWeb* +framework generates SQL from RQL to fetch data from relation databases). RQL is +focused on browsing relations. The user needs only to know about the *CubicWeb* +data model he is querying, but not about the underlying SQL model. + +Sparql +`````` + +The query language most similar to RQL is SPARQL_, defined by the W3C to serve +for the semantic web. Versa ````` -We should look in more detail, but here are already some ideas for -the moment ... Versa_ is the language most similar to what we wanted -to do, but the model underlying data being RDF, there is some -number of things such as namespaces or handling of the RDF types which -does not interest us. On the functionality level, Versa_ is very comprehensive -including through many functions of conversion and basic types manipulation, -which may need to be guided at one time or another. -Finally, the syntax is a little esoteric. -Sparql -`````` -The query language most similar to RQL is SPARQL_, defined by the W3C to serve -for the semantic web. +We should look in more detail, but here are already some ideas for the moment +... Versa_ is the language most similar to what we wanted to do, but the model +underlying data being RDF, there is some number of things such as namespaces or +handling of the RDF types which does not interest us. On the functionality +level, Versa_ is very comprehensive including through many functions of +conversion and basic types manipulation, which may need to be guided at one time +or another. Finally, the syntax is a little esoteric. The different types of queries @@ -59,7 +56,7 @@ Delete entities or relationship (`DELETE`) Remove entities or relations existing in the database. - + .. _Versa: http://uche.ogbuji.net/tech/rdf/versa/ diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/annexes/rql/language.rst --- a/doc/book/en/annexes/rql/language.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/annexes/rql/language.rst Tue Aug 04 15:06:09 2009 +0200 @@ -67,7 +67,7 @@ of logical operators (see :ref:`PriorityOperators`). Mathematical Operators -```````````````````` +``````````````````````` :: +, -, *, / @@ -81,7 +81,7 @@ * The operator `=` is the default operator. * The operator `LIKE` equivalent to `~=` can be used with the - special character `%` in a string to indicate that the chain + special character `%` in a string to indicate that the chain must start or finish by a prefix/suffix: :: @@ -90,11 +90,11 @@ * The operator `IN` provides a list of possible values: :: - + Any X WHERE X name IN ( 'chauvat', 'fayolle', 'di mascio', 'thenault') -XXX nico: "A trick <> 'bar'" wouldn't it be more convenient than +XXX nico: "A trick <> 'bar'" wouldn't it be more convenient than "NOT A trick 'bar'" ? .. _PriorityOperators: @@ -130,7 +130,7 @@ Type of selected variables. The special type `Any` is equivalent to not specify a type. :restriction: - list of conditions to test successively + list of conditions to test successively `V1 relation V2 | ` :orderterms: Definition of the selection order: variable or column number followed by @@ -167,7 +167,7 @@ Identity ```````` -You can use the special relation `identity` in a query to +You can use the special relation `identity` in a query to add an identity constraint between two variables. This is equivalent to ``is`` in python:: @@ -181,23 +181,23 @@ Limit / offset `````````````` :: - + Any P ORDERBY N LIMIT 5 OFFSET 10 WHERE P is Person, P firstname N Function calls `````````````` :: - + Any UPPER(N) WHERE P firstname N Functions on string: UPPER, LOWER - + Exists `````` :: - + Any X ORDERBY PN,N - WHERE X num N, X version_of P, P name PN, + WHERE X num N, X version_of P, P name PN, EXISTS(X in_state S, S name IN ("dev", "ready")) OR EXISTS(T tags X, T name "priority") @@ -219,12 +219,12 @@ Any C, P WHERE C is Card, P? documented_by C Any T,P,V WHERE T is Ticket, T concerns P, T done_in V? - - + + Having `````` :: - + Any X GROUPBY X WHERE X knows Y HAVING COUNT(Y) > 10 Subqueries @@ -232,14 +232,14 @@ :: (Any X WHERE X is Person) UNION (Any X WHERE X is Company) - + DISTINCT Any W, REF - WITH W, REF BEING + WITH W, REF BEING ( - (Any W, REF WHERE W is Workcase, W ref REF, + (Any W, REF WHERE W is Workcase, W ref REF, W concerned_by D, D name "Logilab") - UNION + UNION (Any W, REF WHERE W is Workcase, W ref REF, ' W split_into WP, WP name "WP1") ) @@ -317,7 +317,7 @@ The restriction can define variables used in assignments. -Caution, if a restriction is specified, the insertion is done for +Caution, if a restriction is specified, the insertion is done for *each line result returned by the restriction*. - *Insert a new person named 'foo'* @@ -331,7 +331,7 @@ INSERT Person X, Person Y: X name 'foo', Y name 'nice', X friend Y -- *Insert a new person named 'foo' and a 'friend' relation with an existing +- *Insert a new person named 'foo' and a 'friend' relation with an existing person called 'nice'* :: @@ -350,7 +350,7 @@ SET X name 'bar', X first_name 'original' WHERE X is Person, X name 'foo' -- *Insert a relation of type 'know' between objects linked by +- *Insert a relation of type 'know' between objects linked by the relation of type 'friend'* :: diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/conf.py --- a/doc/book/en/conf.py Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/conf.py Tue Aug 04 15:06:09 2009 +0200 @@ -51,7 +51,7 @@ # The short X.Y version. version = '0.54' # The full version, including alpha/beta/rc tags. -release = '3.2' +release = '3.4' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/cubes/available-cubes.rst --- a/doc/book/en/development/cubes/available-cubes.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/cubes/available-cubes.rst Tue Aug 04 15:06:09 2009 +0200 @@ -2,7 +2,7 @@ Available cubes --------------- -An application is based on several basic cubes. In the set of available +An instance is based on several basic cubes. In the set of available basic cubes we can find for example : Base entity types diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/cubes/cc-newcube.rst --- a/doc/book/en/development/cubes/cc-newcube.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/cubes/cc-newcube.rst Tue Aug 04 15:06:09 2009 +0200 @@ -7,22 +7,21 @@ cubicweb-ctl newcube mycube - # answer questions + # answer questions hg init moncube cd mycube hg add . hg ci -If all went well, you should see the cube you just create in the list -returned by `cubicweb-ctl list` in the section *Available components*, +If all went well, you should see the cube you just created in the list +returned by ``cubicweb-ctl list`` in the section *Available components*, and if it is not the case please refer to :ref:`ConfigurationEnv`. -To use a cube, you have to list it in the variable ``__use__`` -of the file ``__pkginfo__.py`` of the instance. -This variable is used for the instance packaging (dependencies -handled by system utility tools such as APT) and the usable cubes -at the time the base is created (import_erschema('MyCube') will -not properly work otherwise). +To reuse an existing cube, add it to the list named ``__use__`` and defined in +:file:`__pkginfo__.py`. This variable is used for the instance packaging +(dependencies handled by system utility tools such as APT) and the usable cubes +at the time the base is created (import_erschema('MyCube') will not properly +work otherwise). .. note:: Please note that if you do not wish to use default directory @@ -31,12 +30,12 @@ the source code of your cube: ``cubicweb-ctl newcube --directory=/path/to/cubes/library cube_name`` - + Usage of :command:`cubicweb-ctl liveserver` ------------------------------------------- To quickly test a new cube, you can also use the `liveserver` command for cubicweb-ctl -which allows to create an instance in memory (using an SQLite database by +which allows to create an instance in memory (using an SQLite database by default) and make it accessible through a web server :: cubicweb-ctl live-server mycube diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/cubes/layout.rst --- a/doc/book/en/development/cubes/layout.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/cubes/layout.rst Tue Aug 04 15:06:09 2009 +0200 @@ -80,7 +80,7 @@ * ``entities`` contains the entities definition (server side and web interface) * ``sobjects`` contains hooks and/or views notifications (server side only) * ``views`` contains the web interface components (web interface only) -* ``test`` contains tests related to the application (not installed) +* ``test`` contains tests related to the cube (not installed) * ``i18n`` contains message catalogs for supported languages (server side and web interface) * ``data`` contains data files for static content (images, css, javascripts) diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/datamodel/baseschema.rst --- a/doc/book/en/development/datamodel/baseschema.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/datamodel/baseschema.rst Tue Aug 04 15:06:09 2009 +0200 @@ -1,9 +1,9 @@ -Pre-defined schemas in the library ----------------------------------- +Pre-defined entities in the library +----------------------------------- The library defines a set of entity schemas that are required by the system -or commonly used in `CubicWeb` applications. +or commonly used in *CubicWeb* instances. Entity types used to store the schema @@ -18,21 +18,21 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * `CWUser`, system users * `CWGroup`, users groups -* `CWPermission`, used to configure the security of the application +* `CWPermission`, used to configure the security of the instance Entity types used to manage workflows ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * `State`, workflow state * `Transition`, workflow transition -* `TrInfo`, record of a transition trafic for an entity +* `TrInfo`, record of a transition trafic for an entity Other entity types ~~~~~~~~~~~~~~~~~~ -* `CWCache` -* `CWProperty`, used to configure the application +* `CWCache`, cache entities used to improve performances +* `CWProperty`, used to configure the instance * `EmailAddress`, email address, used by the system to send notifications to the users and also used by others optionnals schemas * `Bookmark`, an entity type used to allow a user to customize his links within - the application + the instance diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/datamodel/define-workflows.rst --- a/doc/book/en/development/datamodel/define-workflows.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/datamodel/define-workflows.rst Tue Aug 04 15:06:09 2009 +0200 @@ -2,36 +2,36 @@ .. _Workflow: -An Example: Workflow definition -=============================== +Define a Workflow +================= General ------- -A workflow describes how certain entities have to evolve between -different states. Hence we have a set of states, and a "transition graph", +A workflow describes how certain entities have to evolve between +different states. Hence we have a set of states, and a "transition graph", i.e. a list of possible transitions from one state to another state. -We will define a simple workflow for a blog, with only the following -two states: `submitted` and `published`. So first, we create a simple -`CubicWeb` in ten minutes (see :ref:`BlogTenMinutes`). +We will define a simple workflow for a blog, with only the following +two states: `submitted` and `published`. So first, we create a simple +*CubicWeb* in ten minutes (see :ref:`BlogFiveMinutes`). Set-up a workflow ----------------- -We want to create a workflow to control the quality of the BlogEntry -submitted on your application. When a BlogEntry is created by a user +We want to create a workflow to control the quality of the BlogEntry +submitted on your instance. When a BlogEntry is created by a user its state should be `submitted`. To be visible to all, it has to be in the state `published`. To move it from `submitted` to `published`, we need a transition that we can call `approve_blogentry`. A BlogEntry state should not be modifiable by every user. -So we have to define a group of users, `moderators`, and +So we have to define a group of users, `moderators`, and this group will have appropriate permissions to publish a BlogEntry. There are two ways to create a workflow: from the user interface, -or by defining it in ``migration/postcreate.py``. -This script is executed each time a new ``cubicweb-ctl db-init`` is done. +or by defining it in ``migration/postcreate.py``. +This script is executed each time a new ``cubicweb-ctl db-init`` is done. We strongly recommand to create the workflow in ``migration/postcreate.py`` and we will now show you how. Read `Under the hood`_ to understand why. @@ -60,13 +60,13 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``postcreate.py`` script is executed in a special environment, adding -several `CubicWeb` primitives that can be used. +several *CubicWeb* primitives that can be used. They are all defined in the ``class ServerMigrationHelper``. We will only discuss the methods we use to create a workflow in this example. To define our workflow for BlogDemo, please add the following lines to ``migration/postcreate.py``:: - + _ = unicode moderators = add_entity('CWGroup', name=u"moderators") @@ -88,12 +88,12 @@ add_transition(_('approve_blogentry'), 'BlogEntry', (submitted,), published, ('moderators', 'managers'),) -``add_transition`` expects +``add_transition`` expects * as the first argument the name of the transition, then the entity type on which the transition can be applied, * then the list of states on which the transition can be trigged, - * the target state of the transition, + * the target state of the transition, * and the permissions (e.g. a list of user groups who can apply the transition; the user has to belong to at least one of the listed group to perform the action). @@ -106,11 +106,11 @@ Do not forget to add the `_()` in front of all states and transitions names while creating a workflow so that they will be identified by the i18n catalog scripts. -In addition to the user group condition, we could have added a RQL condition. -In this case, the user can only perform the action if -the two conditions are satisfied. +In addition to the user group condition, we could have added a RQL condition. +In this case, the user can only perform the action if +the two conditions are satisfied. -If we use a RQL condition on a transition, we can use the following +If we use a RQL condition on a transition, we can use the following variables: * `%(eid)s`, object's eid @@ -118,12 +118,12 @@ * `%(seid)s`, the object's current state eid -.. image:: images/lax-book.03-transitions-view.en.png +.. image:: ../../images/lax-book.03-transitions-view.en.png You can notice that in the action box of a BlogEntry, the state is now listed as well as the possible transitions defined by the workflow. The transitions will only be displayed for users having the right permissions. -In our example, the transition `approve_blogentry` will only be displayed +In our example, the transition `approve_blogentry` will only be displayed for the users belonging to the group `moderators` or `managers`. @@ -131,7 +131,7 @@ ~~~~~~~~~~~~~~ A workflow is a collection of entities of type ``State`` and of type ``Transition`` -which are standard `CubicWeb` entity types. +which are standard *CubicWeb* entity types. For instance, the following lines:: submitted = add_state(_('submitted'), 'BlogEntry', initial=True) @@ -141,7 +141,7 @@ with name 'published'. Whereas:: add_transition(_('approve_blogentry'), 'BlogEntry', (submitted,), published, ('moderators', 'managers'),) - + will create an entity of type ``Transition`` with name 'approve_blogentry' which will be linked to the ``State`` entities created before. As a consequence, we could use the administration interface to do these operations. @@ -151,8 +151,8 @@ Indeed, if you create the states and transitions through the user interface, next time you initialize the database -you will have to re-create all the entities. -The user interface should only be a reference for you to view the states +you will have to re-create all the entities. +The user interface should only be a reference for you to view the states and transitions, but is not the appropriate interface to define your application workflow. diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/datamodel/definition.rst --- a/doc/book/en/development/datamodel/definition.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/datamodel/definition.rst Tue Aug 04 15:06:09 2009 +0200 @@ -3,30 +3,29 @@ Yams *schema* ------------- -The **schema** is the core piece of a `CubicWeb` application as it defines +The **schema** is the core piece of a *CubicWeb* instance as it defines the handled data model. It is based on entity types that are either already -defined in the `CubicWeb` standard library; or more specific types, that -`CubicWeb` expects to find in one or more Python files under the directory +defined in the *CubicWeb* standard library; or more specific types, that +*CubicWeb* expects to find in one or more Python files under the directory `schema`. At this point, it is important to make clear the difference between *relation type* and *relation definition*: a *relation type* is only a relation -name with potentially other additionnal properties (see XXXX), whereas a -*relation definition* is a complete triplet -" ". -A relation type could have been implied if none is related to a +name with potentially other additionnal properties (see XXXX), whereas a +*relation definition* is a complete triplet +" ". +A relation type could have been implied if none is related to a relation definition of the schema. -All `CubicWeb` built-in types are available : `String`, `Int`, `Float`, -`Decimal`, `Boolean`, `Date`, `Datetime`, `Time`, `Interval`, `Byte` +All *CubicWeb* built-in types are available : `String`, `Int`, `Float`, +`Decimal`, `Boolean`, `Date`, `Datetime`, `Time`, `Interval`, `Byte` and `Password`. They are implicitely imported (as well as the special the function "_" for translation :ref:`internationalization`). -The instance schema of an application is defined on all appobjects by a .schema -class attribute set on registration. It's an instance of -:class:`yams.schema.Schema`. +The instance schema is defined on all appobjects by a .schema class attribute set +on registration. It's an instance of :class:`yams.schema.Schema`. Entity type ~~~~~~~~~~~ @@ -67,7 +66,7 @@ RelationSchema.rproperties() RelationSchema.rproperty(subjtype, objtype, property name) -* Optional properties for attributes and relations : +* Optional properties for attributes and relations : - `description` : a string describing an attribute or a relation. By default this string will be used in the editing form of the entity, which means @@ -79,7 +78,7 @@ - `cardinality` : a two character string which specify the cardinality of the relation. The first character defines the cardinality of the relation on - the subject, and the second on the object. When a relation can have + the subject, and the second on the object. When a relation can have multiple subjects or objects, the cardinality applies to all, not on a one-to-one basis (so it must be consistent...). The possible values are inspired from regular expression syntax : @@ -92,24 +91,24 @@ - `meta` : boolean indicating that the relation is a meta-relation (false by default) -* optional properties for attributes : +* optional properties for attributes : - `required` : boolean indicating if the attribute is required (false by default) - `unique` : boolean indicating if the value of the attribute has to be unique or not within all entities of the same type (false by default) - - `indexed` : boolean indicating if an index needs to be created for this + - `indexed` : boolean indicating if an index needs to be created for this attribute in the database (false by default). This is useful only if you know that you will have to run numerous searches on the value of this attribute. - `default` : default value of the attribute. In case of date types, the values which could be used correspond to the RQL keywords `TODAY` and `NOW`. - + - `vocabulary` : specify static possible values of an attribute -* optional properties of type `String` : +* optional properties of type `String` : - `fulltextindexed` : boolean indicating if the attribute is part of the full text index (false by default) (*applicable on the type `Byte` @@ -120,11 +119,11 @@ - `maxsize` : integer providing the maximum size of the string (no limit by default) -* optional properties for relations : +* optional properties for relations : - `composite` : string indicating that the subject (composite == 'subject') is composed of the objects of the relations. For the opposite case (when - the object is composed of the subjects of the relation), we just set + the object is composed of the subjects of the relation), we just set 'object' as value. The composition implies that when the relation is deleted (so when the composite is deleted), the composed are also deleted. @@ -137,7 +136,7 @@ * `SizeConstraint` : allows to specify a minimum and/or maximum size on string (generic case of `maxsize`) -* `BoundConstraint` : allows to specify a minimum and/or maximum value on +* `BoundConstraint` : allows to specify a minimum and/or maximum value on numeric types * `UniqueConstraint` : identical to "unique=True" @@ -146,7 +145,7 @@ * `RQLConstraint` : allows to specify a RQL query that has to be satisfied by the subject and/or the object of the relation. In this query the variables - `S` and `O` are reserved for the entities subject and object of the + `S` and `O` are reserved for the entities subject and object of the relation. * `RQLVocabularyConstraint` : similar to the previous type of constraint except @@ -160,7 +159,7 @@ The security model ~~~~~~~~~~~~~~~~~~ -The security model of `cubicWeb` is based on `Access Control List`. +The security model of `cubicWeb` is based on `Access Control List`. The main principles are: * users and groups of users @@ -168,7 +167,7 @@ * permissions (read, update, create, delete) * permissions are assigned to groups (and not to users) -For `CubicWeb` in particular: +For *CubicWeb* in particular: * we associate rights at the enttities/relations schema level * for each entity, we distinguish four kind of permissions: read, @@ -213,13 +212,13 @@ This can only be used for the actions `update` and `delete` of an entity type. -It is also possible to use specific groups if they are defined in the precreate +It is also possible to use specific groups if they are defined in the precreate of the cube (``migration/precreate.py``). Use of RQL expression for writing rights ````````````````````````````````````````` -It is possible to define RQL expression to provide update permission +It is possible to define RQL expression to provide update permission (`add`, `delete` and `update`) on relation and entity types. RQL expression for entity type permission : @@ -232,13 +231,13 @@ respectively on the current entity (on which the action is verified) and on the user who send the request -* it is possible to use, in this expression, a special relation - "has__permission" where the subject is the user and the +* it is possible to use, in this expression, a special relation + "has__permission" where the subject is the user and the object is a any variable, meaning that the user needs to have permission to execute the action on the entities related - to this variable + to this variable -For RQL expressions on a relation type, the principles are the same except +For RQL expressions on a relation type, the principles are the same except for the following : * you have to use the class `RQLExpression` in the case of a non-final relation @@ -248,7 +247,7 @@ which the action is being verified) and the user who executed the query * we can also defined rights on attributes of an entity (non-final relation), - knowing that : + knowing that : - to defines RQL expression, we have to use the class `RQLExpression` in which X represents the entity the attribute belongs to @@ -260,17 +259,17 @@ Potentially, the use of an RQL expression to add an entity or a relation can cause problems for the user interface, because if the expression uses - the entity or the relation to create, then we are not able to verify the + the entity or the relation to create, then we are not able to verify the permissions before we actually add the entity (please note that this is not a problem for the RQL server at all, because the permissions checks are - done after the creation). In such case, the permission check methods - (check_perm, has_perm) can indicate that the user is not allowed to create - this entity but can obtain the permission. + done after the creation). In such case, the permission check methods + (check_perm, has_perm) can indicate that the user is not allowed to create + this entity but can obtain the permission. To compensate this problem, it is usually necessary, for such case, to use an action that reflects the schema permissions but which enables to check properly the permissions so that it would show up if necessary. - + Use of RQL expression for reading rights ```````````````````````````````````````` @@ -315,20 +314,20 @@ `Company` through the semantic `works_for`. The name of the Python attribute corresponds to the name of the attribute -or the relation in `CubicWeb` application. +or the relation in *CubicWeb* application. An attribute is defined in the schema as follows:: - + attr_name = attr_type(properties*) where `attr_type` is one of the type listed above and `properties` is a list of the attribute needs to statisfy (see :ref:`properties` -for more details). +for more details). * relations can be defined by using `ObjectRelation` or `SubjectRelation`. The first argument of `SubjectRelation` or `ObjectRelation` gives respectively - the object/subject entity type of the relation. This could be : + the object/subject entity type of the relation. This could be : * a string corresponding to an entity type @@ -337,7 +336,7 @@ * special string such as follows : - "**" : all types of entities - - "*" : all types of non-meta entities + - "*" : all types of non-meta entities - "@" : all types of meta entities but not system entities (e.g. used for the basic schema description) @@ -356,9 +355,9 @@ A relation is defined by a Python class heriting `RelationType`. The name of the class corresponds to the name of the type. The class then contains -a description of the properties of this type of relation, and could as well +a description of the properties of this type of relation, and could as well contain a string for the subject and a string for the object. This allows to create -new definition of associated relations, (so that the class can have the +new definition of associated relations, (so that the class can have the definition properties from the relation) for example :: class locked_by(RelationType): diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/datamodel/index.rst --- a/doc/book/en/development/datamodel/index.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/datamodel/index.rst Tue Aug 04 15:06:09 2009 +0200 @@ -9,5 +9,5 @@ definition metadata baseschema - -.. define-workflows + define-workflows + inheritance diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/datamodel/inheritance.rst --- a/doc/book/en/development/datamodel/inheritance.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/datamodel/inheritance.rst Tue Aug 04 15:06:09 2009 +0200 @@ -1,1 +1,8 @@ -XXX WRITME \ No newline at end of file + +Inheritance +----------- + +When describing a data model, entities can inherit from other entities as is +common in object-oriented programming. + +XXX WRITME diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/devcore/appobject.rst --- a/doc/book/en/development/devcore/appobject.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/devcore/appobject.rst Tue Aug 04 15:06:09 2009 +0200 @@ -1,5 +1,5 @@ - + The `AppObject` class ~~~~~~~~~~~~~~~~~~~~~ @@ -19,9 +19,9 @@ At the recording, the following attributes are dynamically added to the *subclasses*: -* `vreg`, the `vregistry` of the application -* `schema`, the application schema -* `config`, the application configuration +* `vreg`, the `vregistry` of the instance +* `schema`, the instance schema +* `config`, the instance configuration We also find on instances, the following attributes: @@ -36,7 +36,7 @@ can be specified through the special parameter `method` (the connection is theoretically done automatically :). - * `datadir_url()`, returns the directory of the application data + * `datadir_url()`, returns the directory of the instance data (contains static files such as images, css, js...) * `base_url()`, shortcut to `req.base_url()` @@ -57,9 +57,9 @@ :Data formatting: * `format_date(date, date_format=None, time=False)` returns a string for a - mx date time according to application's configuration + mx date time according to instance's configuration * `format_time(time)` returns a string for a mx date time according to - application's configuration + instance's configuration :And more...: diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/devcore/dbapi.rst --- a/doc/book/en/development/devcore/dbapi.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/devcore/dbapi.rst Tue Aug 04 15:06:09 2009 +0200 @@ -20,7 +20,7 @@ The `Connection` object owns the methods `commit` and `rollback`. You *should never need to use them* during the development of the web interface based on -the `CubicWeb` framework as it determines the end of the transaction depending +the *CubicWeb* framework as it determines the end of the transaction depending on the query execution success. .. note:: diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/devcore/index.rst --- a/doc/book/en/development/devcore/index.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/devcore/index.rst Tue Aug 04 15:06:09 2009 +0200 @@ -5,12 +5,11 @@ :maxdepth: 1 vreg.rst - selection.rst appobject.rst selectors.rst dbapi.rst - + :mod:`Configuration ` ---------------------------------------- diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/devcore/selectors.rst --- a/doc/book/en/development/devcore/selectors.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/devcore/selectors.rst Tue Aug 04 15:06:09 2009 +0200 @@ -7,7 +7,7 @@ essential part of the construction of well behaved cubes. -`CubicWeb` provides its own set of selectors that you can use and here is a +*CubicWeb* provides its own set of selectors that you can use and here is a description of some of the most common used: Of course you will write your own set of selectors as you get familiar with the diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/devcore/vreg.rst --- a/doc/book/en/development/devcore/vreg.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/devcore/vreg.rst Tue Aug 04 15:06:09 2009 +0200 @@ -1,3 +1,5 @@ +.. -*- coding: utf-8 -*- + The VRegistry -------------- @@ -7,23 +9,25 @@ Details of the recording process ```````````````````````````````` -* par défaut on enregistre automatiquement tout les objets +XXX this part needs to be updated and checked + +* by default all objects are registered automatically -* si certains objets doivent remplacer d'autres objets ou être inclus - conditionnellement, - - enregistrement explicite en définissant la fonction `registration_callback(vreg)` - - appel des méthodes d'enregistrement des objets sur le vreg +* if some objects have to replace other objects or be included only if a + condition is true, + - explicitly register the object by defining `registration_callback(vreg)` + - call registration methods on objects listed in the vreg registry + .. note:: Once the function `registration_callback(vreg)` is implemented, all the objects - need to be explicitly registered as it disables the automatic object registering. - -* suppression de l'ancien système quand il ne restera plus de réference au - module registerers dans le code des cubes existants. + have to be explicitly registered as it disables the automatic object registering. +* the old registration mechanism will be removed when there will be no reference + left to the registerers module in cubicweb and the library of cubes. -Examples +Examples: -.. code-block:: python +.. sourcecode:: python # web/views/basecomponents.py def registration_callback(vreg): @@ -40,7 +44,8 @@ API d'enregistrement des objets ``````````````````````````````` -.. code-block:: python + +.. sourcecode:: python register(obj, registryname=None, oid=None, clear=False) @@ -58,7 +63,8 @@ Defining selectors `````````````````` -The object's selector is defined by itsd `__select__` class attribute. + +The object's selector is defined by its `__select__` class attribute. When two selectors are combined using the `&` operator (former `chainall`), it means that both should return a positive score. On success, the sum of scores is returned. @@ -86,18 +92,20 @@ Example ```````` -Le but final : quand on est sur un Blog, on veut que le lien rss de celui-ci pointe -vers les entrées de ce blog, non vers l'entité blog elle-même. +XXX this part needs to be translated -L'idée générale pour résoudre ça : on définit une méthode sur les classes d'entité +Le but final : quand on est sur un Blog, on veut que le lien rss de celui-ci pointe +vers les entrées de ce blog, non vers l'entité blog elle-même. + +L'idée générale pour résoudre ça : on définit une méthode sur les classes d'entité qui renvoie l'url du flux rss pour l'entité en question. Avec une implémentation -par défaut sur AnyEntity et une implémentation particulière sur Blog qui fera ce +par défaut sur AnyEntity et une implémentation particulière sur Blog qui fera ce qu'on veut. -La limitation : on est embêté dans le cas ou par ex. on a un result set qui contient +La limitation : on est embêté dans le cas ou par ex. on a un result set qui contient plusieurs entités Blog (ou autre chose), car on ne sait pas sur quelle entité appeler la méthode sus-citée. Dans ce cas, on va conserver le comportement actuel (eg appel -à limited_rql) +à limited_rql) Donc : on veut deux cas ici, l'un pour un rset qui contient une et une seule entité, l'autre pour un rset qui contient plusieurs entité. @@ -116,7 +124,7 @@ pour voir le détail) * non_final_entity, qui filtre sur des rset contenant une liste d'entité non finale -ça correspond donc à notre 2eme cas. Reste à fournir un composant plus spécifique +ça correspond donc à notre 2eme cas. Reste à fournir un composant plus spécifique pour le 1er cas :: class EntityRSSIconBox(RSSIconBox): @@ -130,11 +138,11 @@ non sélectionnable). Donc ici, sur un rset avec plusieurs entités, onelinerset_selector rendra la classe EntityRSSIconBox non sélectionnable, et on obtiendra bien la classe RSSIconBox. Pour un rset avec une entité, la classe EntityRSSIconBox aura un -score supérieur à RSSIconBox et c'est donc bien elle qui sera sélectionnée. +score supérieur à RSSIconBox et c'est donc bien elle qui sera sélectionnée. -Voili voilou, il reste donc pour finir tout ça : +Voili voilou, il reste donc pour finir tout ça : -* à définir le contenu de la méthode call de EntityRSSIconBox +* à définir le contenu de la méthode call de EntityRSSIconBox * fournir l'implémentation par défaut de la méthode renvoyant l'url du flux rss sur AnyEntity * surcharger cette methode dans blog.Blog @@ -144,8 +152,8 @@ ``````````````````````` Il faut utiliser les sélecteurs pour faire des choses différentes en -fonction de ce qu'on a en entrée. Dès qu'on a un "if" qui teste la -nature de `self.rset` dans un objet, il faut très sérieusement se +fonction de ce qu'on a en entrée. Dès qu'on a un "if" qui teste la +nature de `self.rset` dans un objet, il faut très sérieusement se poser la question s'il ne vaut pas mieux avoir deux objets différent avec des sélecteurs approprié. diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/devrepo/hooks.rst --- a/doc/book/en/development/devrepo/hooks.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/devrepo/hooks.rst Tue Aug 04 15:06:09 2009 +0200 @@ -10,8 +10,8 @@ *Hooks* are executed before or after updating an entity or a relation in the repository. -Their prototypes are as follows: - +Their prototypes are as follows: + * after_add_entity (session, entity) * after_update_entity (session, entity) * after_delete_entity (session, eid) @@ -23,10 +23,10 @@ * after_delete_relation (session, fromeid, rtype, toeid) * before_add_relation (session, fromeid, rtype, toeid) * before_delete_relation (session, fromeid, rtype, toeid) - + * server_startup * server_shutdown - + * session_open * session_close diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/devrepo/sessions.rst --- a/doc/book/en/development/devrepo/sessions.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/devrepo/sessions.rst Tue Aug 04 15:06:09 2009 +0200 @@ -3,7 +3,25 @@ Sessions ======== +There are three kinds of sessions. + +* `user sessions` are the most common: they are related to users and + carry security checks coming with user credentials + +* `super sessions` are children of ordinary user sessions and allow to + bypass security checks (they are created by calling unsafe_execute + on a user session); this is often convenient in hooks which may + touch data that is not directly updatable by users + +* `internal sessions` have all the powers; they are also used in only a + few situations where you don't already have an adequate session at + hand, like: user authentication, data synchronisation in + multi-source contexts + +.. note:: + Do not confuse the session type with their connection mode, for + instance : 'in memory' or 'pyro'. + [WRITE ME] * authentication and management of sessions - diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/devweb/form.rst --- a/doc/book/en/development/devweb/form.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/devweb/form.rst Tue Aug 04 15:06:09 2009 +0200 @@ -9,9 +9,9 @@ * `vocabulary(rtype, x='subject', limit=None)`, called by the editing views, it returns a list of couples (label, eid) of entities that could be related to the entity by the relation `rtype` - * `subject_relation_vocabulary(rtype, limit=None)`, called internally + * `subject_relation_vocabulary(rtype, limit=None)`, called internally by `vocabulary` in the case of a subject relation - * `object_relation_vocabulary(rtype, limit=None)`, called internally + * `object_relation_vocabulary(rtype, limit=None)`, called internally by `vocabulary` in the case of an object relation * `relation_vocabulary(rtype, targettype, x, limit=None)`, called internally by `subject_relation_vocabulary` and `object_relation_vocabulary` diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/devweb/index.rst --- a/doc/book/en/development/devweb/index.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/devweb/index.rst Tue Aug 04 15:06:09 2009 +0200 @@ -1,7 +1,7 @@ Web development =============== -In this chapter, we will core api for web development in the CubicWeb framework. +In this chapter, we will describe the core api for web development in the *CubicWeb* framework. .. toctree:: :maxdepth: 1 diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/devweb/internationalization.rst --- a/doc/book/en/development/devweb/internationalization.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/devweb/internationalization.rst Tue Aug 04 15:06:09 2009 +0200 @@ -1,12 +1,12 @@ .. -*- coding: utf-8 -*- -.. _internationalisation: +.. _internationalization: -Internationalisation +Internationalization --------------------- -Cubicweb fully supports the internalization of it's content and interface. +Cubicweb fully supports the internalization of its content and interface. Cubicweb's interface internationalization is based on the translation project `GNU gettext`_. @@ -16,7 +16,7 @@ * in your Python code and cubicweb-tal templates : mark translatable strings -* in your application : handle the translation catalog +* in your instance : handle the translation catalog String internationalization ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -66,16 +66,18 @@ .. note:: We dont need to mark the translation strings of entities/relations - used by a particular application's schema as they are generated + used by a particular instance's schema as they are generated automatically. +If you need to add messages on top of those that can be found in the source, +you can create a file named `i18n/static-messages.pot`. Handle the translation catalog ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Once the internationalization is done in your application's code, you need -to populate and update the translation catalog. Cubicweb provides the -following commands for this purpose: +Once the internationalization is done in your code, you need to populate and +update the translation catalog. Cubicweb provides the following commands for this +purpose: * `i18ncubicweb` updates Cubicweb framework's translation @@ -83,28 +85,28 @@ need to use this command. * `i18ncube` updates the translation catalogs of *one particular - component* (or of all components). After this command is + cube* (or of all cubes). After this command is executed you must update the translation files *.po* in the "i18n" directory of your template. This command will of course not remove existing translations still in use. * `i18ninstance` recompile the translation catalogs of *one particular instance* (or of all instances) after the translation catalogs of - its components have been updated. This command is automatically + its cubes have been updated. This command is automatically called every time you create or update your instance. The compiled catalogs (*.mo*) are stored in the i18n//LC_MESSAGES of - application where `lang` is the language identifier ('en' or 'fr' + instance where `lang` is the language identifier ('en' or 'fr' for exemple). Example ``````` -You have added and/or modified some translation strings in your application -(after creating a new view or modifying the application's schema for exemple). +You have added and/or modified some translation strings in your cube +(after creating a new view or modifying the cube's schema for exemple). To update the translation catalogs you need to do: -1. `cubicweb-ctl i18ncube ` -2. Edit the /xxx.po files and add missing translations (empty `msgstr`) +1. `cubicweb-ctl i18ncube ` +2. Edit the /i18n/xxx.po files and add missing translations (empty `msgstr`) 3. `hg ci -m "updated i18n catalogs"` -4. `cubicweb-ctl i18ninstance ` +4. `cubicweb-ctl i18ninstance ` diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/devweb/views.rst --- a/doc/book/en/development/devweb/views.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/devweb/views.rst Tue Aug 04 15:06:09 2009 +0200 @@ -1,13 +1,16 @@ + +.. _Views: + Views ----- This chapter aims to describe the concept of a `view` used all along the development of a web application and how it has been implemented -in `CubicWeb`. +in *CubicWeb*. We'll start with a description of the interface providing you with a basic understanding of the classes and methods available, then detail the view -selection principle which makes `CubicWeb` web interface very flexible. +selection principle which makes *CubicWeb* web interface very flexible. A `View` is an object applied to another object such as an entity. @@ -42,19 +45,19 @@ * `dispatch(**context)`, render the view by calling `call` or `cell_call` depending on the given parameters -* `call(**kwargs)`, call the view for a complete result set or null (default +* `call(**kwargs)`, call the view for a complete result set or null (default implementation calls `cell_call()` on each cell of the result set) * `cell_call(row, col, **kwargs)`, call the view for a given cell of a result set * `url()`, returns the URL enabling us to get the view with the current - result set -* `view(__vid, rset, __fallback_vid=None, **kwargs)`, call the view of identifier + result set +* `view(__vid, rset, __fallback_vid=None, **kwargs)`, call the view of identifier `__vid` on the given result set. It is possible to give a view identifier of fallback that will be used if the view requested is not applicable to the result set - + * `wview(__vid, rset, __fallback_vid=None, **kwargs)`, similar to `view` except the flow is automatically passed in the parameters - + * `html_headers()`, returns a list of HTML headers to set by the main template * `page_title()`, returns the title to use in the HTML header `title` @@ -67,17 +70,16 @@ * `EntityView`, view applying to lines or cell containing an entity (e.g. an eid) * `StartupView`, start view that does not require a result set to apply to -* `AnyRsetView`, view applied to any result set +* `AnyRsetView`, view applied to any result set * `EmptyRsetView`, view applied to an empty result set -Examples of views class +Examples of views class ----------------------- - Using `templatable`, `content_type` and HTTP cache configuration -.. code-block:: python - +.. sourcecode:: python class RSSView(XMLView): id = 'rss' @@ -86,13 +88,11 @@ content_type = 'text/xml' http_cache_manager = MaxAgeHTTPCacheManager cache_max_age = 60*60*2 # stay in http cache for 2 hours by default - - Using custom selector -.. code-block:: python - +.. sourcecode:: python class SearchForAssociationView(EntityView): """view called by the edition view when the user asks @@ -111,18 +111,18 @@ We'll show you now an example of a ``primary`` view and how to customize it. -If you want to change the way a ``BlogEntry`` is displayed, just override -the method ``cell_call()`` of the view ``primary`` in ``BlogDemo/views.py`` :: +If you want to change the way a ``BlogEntry`` is displayed, just override +the method ``cell_call()`` of the view ``primary`` in ``BlogDemo/views.py``: -.. code-block:: python +.. sourcecode:: python from cubicweb.view import EntityView from cubicweb.selectors import implements - + class BlogEntryPrimaryView(EntityView): id = 'primary' __select__ =implements('Blog') - + def cell_call(self, row, col): entity = self.entity(row, col) self.w(u'

%s

' % entity.title) @@ -131,7 +131,7 @@ self.w(u'

%s

' % entity.text) The above source code defines a new primary view (`line 03`) for -``BlogEntry`` (`line 05`). +``BlogEntry`` (`line 05`). Since views are applied to result sets which can be tables of data, we have to recover the entity from its (row,col)-coordinates (`line 08`). @@ -148,7 +148,7 @@ Let us now improve the primary view of a blog -.. code-block:: python +.. sourcecode:: python class BlogPrimaryView(EntityView): id = 'primary' @@ -170,9 +170,9 @@ about the schema and infer that such entities have to be of the ``BlogEntry`` kind and retrieves them. -The request returns a selection of data called a result set. At +The request returns a selection of data called a result set. At `line 10` the view 'primary' is applied to this result set to output -HTML. +HTML. **This is to be compared to interfaces and protocols in object-oriented languages. Applying a given view called 'a_view' to all the entities @@ -186,7 +186,7 @@ :alt: a blog and all its entries **Before we move forward, remember that the selection/view principle is -at the core of `CubicWeb`. Everywhere in the engine, data is requested +at the core of *CubicWeb*. Everywhere in the engine, data is requested using the RQL language, then HTML/XML/text/PNG is output by applying a view to the result set returned by the query. That is where most of the flexibility comes from.** @@ -202,7 +202,7 @@ * create view "blogentry table" with title, publish_date, category -We will show that by default the view that displays +We will show that by default the view that displays "Any E,D,C WHERE E publish_date D, E category C" is the table view. Of course, the same can be obtained by calling self.wview('table',rset) @@ -215,9 +215,9 @@ [FILLME] - XML views, binaries... ---------------------- + For views generating other formats than HTML (an image generated dynamically for example), and which can not simply be included in the HTML page generated by the main template (see above), you have to: @@ -226,7 +226,7 @@ * set, through the attribute `content_type` of the class, the MIME type generated by the view to `application/octet-stream` -For views dedicated to binary content creation (like dynamically generated +For views dedicated to binary content creation (like dynamically generated images), we have to set the attribute `binary` of the class to `True` (which implies that `templatable == False`, so that the attribute `w` of the view could be replaced by a binary flow instead of unicode). diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/entityclasses/data-as-objects.rst --- a/doc/book/en/development/entityclasses/data-as-objects.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/entityclasses/data-as-objects.rst Tue Aug 04 15:06:09 2009 +0200 @@ -19,18 +19,18 @@ * `absolute_url(**kwargs)`, returns an absolute URL to access the primary view of an entity - + * `rest_path()`, returns a relative REST URL to get the entity * `format(attr)`, returns the format (MIME type) of the field given un parameter - * `printable_value(attr, value=_marker, attrtype=None, format='text/html')`, + * `printable_value(attr, value=_marker, attrtype=None, format='text/html')`, returns a string enabling the display of an attribute value in a given format (the value is automatically recovered if necessary) :Data handling: - * `as_rset()`, converts the entity into an equivalent result set simulating the + * `as_rset()`, converts the entity into an equivalent result set simulating the request `Any X WHERE X eid _eid_` * `complete(skip_bytes=True)`, executes a request that recovers in one time @@ -57,21 +57,21 @@ * `delete()` allows to delete the entity - + Tne :class:`AnyEntity` class ---------------------------- - -To provide a specific behavior for each entity, we have to define -a class inheriting from `cubicweb.entities.AnyEntity`. In general, we -define this class in a module of `mycube.entities` package of an application -so that it will be available on both server and client side. -The class `AnyEntity` is loaded dynamically from the class `Entity` +To provide a specific behavior for each entity, we have to define a class +inheriting from `cubicweb.entities.AnyEntity`. In general, we define this class +in `mycube.entities` module (or in a submodule if we want to split code among +multiple files) so that it will be available on both server and client side. + +The class `AnyEntity` is loaded dynamically from the class `Entity` (`cubciweb.entity`). We define a sub-class to add methods or to specialize the handling of a given entity type The methods defined for `AnyEntity` or `Entity` are the following ones: - + :Standard meta-data (Dublin Core): * `dc_title()`, returns a unicode string corresponding to the meta-data @@ -81,15 +81,15 @@ * `dc_long_title()`, same as dc_title but can return a more detailled title - * `dc_description(format='text/plain')`, returns a unicode string + * `dc_description(format='text/plain')`, returns a unicode string corresponding to the meta-data `Description` (look for a description attribute by default) - * `dc_authors()`, returns a unicode string corresponding to the meta-data + * `dc_authors()`, returns a unicode string corresponding to the meta-data `Authors` (owners by default) - * `dc_date(date_format=None)`, returns a unicode string corresponding to + * `dc_date(date_format=None)`, returns a unicode string corresponding to the meta-data `Date` (update date by default) - * `dc_type(form='')`, returns a string to display the entity type by + * `dc_type(form='')`, returns a string to display the entity type by specifying the preferred form (`plural` for a plural form) diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/entityclasses/index.rst --- a/doc/book/en/development/entityclasses/index.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/entityclasses/index.rst Tue Aug 04 15:06:09 2009 +0200 @@ -2,7 +2,7 @@ =============== In this chapter, we will introduce the objects that are used to handle -the data stored in the database. +the logic associated to the data stored in the database. .. toctree:: :maxdepth: 1 @@ -10,4 +10,4 @@ data-as-objects load-sort interfaces - more \ No newline at end of file + more diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/entityclasses/interfaces.rst --- a/doc/book/en/development/entityclasses/interfaces.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/entityclasses/interfaces.rst Tue Aug 04 15:06:09 2009 +0200 @@ -1,16 +1,19 @@ Interfaces ---------- +Same thing as object-oriented programming interfaces. + XXX how to define a cw interface Declaration of interfaces implemented by a class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + XXX __implements__ Interfaces defined in the library ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. automodule:: cubicweb.interface - :members: -````````````` +automodule:: cubicweb.interface :members: + + diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/entityclasses/load-sort.rst --- a/doc/book/en/development/entityclasses/load-sort.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/entityclasses/load-sort.rst Tue Aug 04 15:06:09 2009 +0200 @@ -1,26 +1,28 @@ + +.. _FetchAttrs: Loaded attributes and default sorting management ```````````````````````````````````````````````` -* The class attribute `fetch_attrs` allows to defined in an entity class - a list of names of attributes or relations that should be automatically - loaded when we recover the entities of this type. In the case of relations, +* The class attribute `fetch_attrs` allows to define in an entity class a list + of names of attributes or relations that should be automatically loaded when + entities of this type are fetched from the database. In the case of relations, we are limited to *subject of cardinality `?` or `1`* relations. * The class method `fetch_order(attr, var)` expects an attribute (or relation) name as a parameter and a variable name, and it should return a string - to use in the requirement `ORDER BY` of an RQL query to automatically + to use in the requirement `ORDERBY` of an RQL query to automatically sort the list of entities of such type according to this attribute, or `None` if we do not want to sort on the attribute given in the parameter. By default, the entities are sorted according to their creation date. -* The class method `fetch_unrelated_order(attr, var)` is similar to the +* The class method `fetch_unrelated_order(attr, var)` is similar to the method `fetch_order` except that it is essentially used to control - the sorting of drop-down lists enabling relations creation in + the sorting of drop-down lists enabling relations creation in the editing view of an entity. The function `fetch_config(fetchattrs, mainattr=None)` simplifies the -definition of the attributes to load and the sorting by returning a +definition of the attributes to load and the sorting by returning a list of attributes to pre-load (considering automatically the attributes of `AnyEntity`) and a sorting function based on the main attribute (the second parameter if specified otherwisethe first attribute from diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/entityclasses/more.rst --- a/doc/book/en/development/entityclasses/more.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/entityclasses/more.rst Tue Aug 04 15:06:09 2009 +0200 @@ -1,12 +1,14 @@ Navigation on deletion ---------------------- + XXX after_deletion_path, pre_web_edit Controlling output url ---------------------- -XXX +----------------------- + +XXX write me Controling notification references ---------------------------------- -XXX +XXX write me diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/index.rst --- a/doc/book/en/development/index.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/index.rst Tue Aug 04 15:06:09 2009 +0200 @@ -4,7 +4,7 @@ Part II - Development --------------------- -This part is about developing web applications with the `CubicWeb` framework. +This part is about developing web applications with the *CubicWeb* framework. .. toctree:: :maxdepth: 2 @@ -18,3 +18,4 @@ testing/index migration/index webstdlib/index + profiling/index diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/migration/index.rst --- a/doc/book/en/development/migration/index.rst Tue Aug 04 11:43:03 2009 +0200 +++ b/doc/book/en/development/migration/index.rst Tue Aug 04 15:06:09 2009 +0200 @@ -5,29 +5,29 @@ Migration ========= -One of the main concept in `CubicWeb` is to create incremental applications. -For this purpose, multiple actions are provided to facilitate the improvement -of an application, and in particular to handle the changes to be applied -to the data model, without loosing existing data. +One of the main design goals of *CubicWeb* was to support iterative and agile +development. For this purpose, multiple actions are provided to facilitate the +improvement of an instance, and in particular to handle the changes to be +applied to the data model, without loosing existing data. -The current version of an application model is provided in the file +The current version of a cube (and of cubicweb itself) is provided in the file `__pkginfo__.py` as a tuple of 3 integers. Migration scripts management ---------------------------- Migration scripts has to be located in the directory `migration` of your -application and named accordingly: +cube and named accordingly: :: [_]_.py -in which : +in which : * X.Y.Z is the model version number to which the script enables to migrate. -* *mode* (between the last "_" and the extension ".py") is used for +* *mode* (between the last "_" and the extension ".py") is used for distributed installation. It indicates to which part of the application (RQL server, web server) the script applies. Its value could be : @@ -44,10 +44,10 @@ (schema and data migration for example). Again in the directory `migration`, the file `depends.map` allows to indicate -that for the migration to a particular model version, you always have to first -migrate to a particular `CubicWeb` version. This file can contain comments (lines +that for the migration to a particular model version, you always have to first +migrate to a particular *CubicWeb* version. This file can contain comments (lines starting by `#`) and a dependancy is listed as follows: :: - + : For example: :: @@ -65,13 +65,10 @@ * `config`, instance configuration * `interactive_mode`, boolean indicating that the script is executed in - an interactive mode or not - -* `appltemplversion`, application model version of the instance + an interactive mode or not -* `templversion`, installed application model version - -* `cubicwebversion`, installed cubicweb version +* `versions_map`, dictionary of migrated versions (key are cubes + names, including 'cubicweb', values are (from version, to version) * `confirm(question)`, function asking the user and returning true if the user answers yes, false otherwise (always returns true in @@ -84,41 +81,39 @@ * `checkpoint`, request confirming and executing a "commit" at checking point -* `repo_schema`, instance persisting schema (e.g. instance schema of the - current migration) +* `schema`, instance schema (readen from the database) -* `newschema`, installed schema on the file system (e.g. schema of +* `fsschema`, installed schema on the file system (e.g. schema of the updated model and cubicweb) -* `sqlcursor`, SQL cursor for very rare cases where it is really - necessary or beneficial to go through the sql - * `repo`, repository object - +* `session`, repository session object + + Schema migration ---------------- The following functions for schema migration are available in `repository` scripts: * `add_attribute(etype, attrname, attrtype=None, commit=True)`, adds a new - attribute to an existing entity type. If the attribute type is not specified, + attribute to an existing entity type. If the attribute type is not specified, then it is extracted from the updated schema. - + * `drop_attribute(etype, attrname, commit=True)`, removes an attribute from an existing entity type. * `rename_attribute(etype, oldname, newname, commit=True)`, renames an attribute - + * `add_entity_type(etype, auto=True, commit=True)`, adds a new entity type. If `auto` is True, all the relations using this entity type and having a known entity type on the other hand will automatically be added. -* `drop_entity_type(etype, commit=True)`, removes an entity type and all the +* `drop_entity_type(etype, commit=True)`, removes an entity type and all the relations using it. * `rename_entity_type(oldname, newname, commit=True)`, renames an entity type - + * `add_relation_type(rtype, addrdef=True, commit=True)`, adds a new relation type. If `addrdef` is True, all the relations definitions of this type will be added. @@ -134,19 +129,12 @@ * `drop_relation_definition(subjtype, rtype, objtype, commit=True)`, removes a relation definition. -* `synchronize_permissions(ertype, commit=True)`, synchronizes permissions on - an entity type or relation type. - -* `synchronize_rschema(rtype, commit=True)`, synchronizes properties and permissions - on a relation type. - -* `synchronize_eschema(etype, commit=True)`, synchronizes properties and persmissions - on an entity type. - -* `synchronize_schema(commit=True)`, synchronizes the persisting schema with the - updated schema (but without adding or removing new entity types, relations types - or even relations definitions). - +* `sync_schema_props_perms(ertype=None, syncperms=True, syncprops=True, syncrdefs=True, commit=True)`, + synchronizes properties and/or permissions on: + - the whole schema if ertype is None + - an entity or relation type schema if ertype is a string + - a relation definition if ertype is a 3-uple (subject, relation, object) + * `change_relation_props(subjtype, rtype, objtype, commit=True, **kwargs)`, changes properties of a relation definition by using the named parameters of the properties to change. @@ -162,7 +150,7 @@ The following functions for data migration are available in `repository` scripts: * `rql(rql, kwargs=None, cachekey=None, ask_confirm=True)`, executes an arbitrary RQL - query, either to interrogate or update. A result set object is returned. + query, either to interrogate or update. A result set object is returned. * `add_entity(etype, *args, **kwargs)`, adds a nes entity type of the given type. The attribute and relation values are specified using the named and @@ -176,8 +164,8 @@ * `add_state(name, stateof, initial=False, commit=False, **kwargs)`, adds a new state in the workflow. - -* `add_transition(name, transitionof, fromstates, tostate, requiredgroups=(), commit=False, **kwargs)`, + +* `add_transition(name, transitionof, fromstates, tostate, requiredgroups=(), commit=False, **kwargs)`, adds a new transition in the workflow. You can find more details about workflows in the chapter :ref:`Workflow` . @@ -185,7 +173,7 @@ Configuration migration ----------------------- -The following functions for configuration migration are available in all +The following functions for configuration migration are available in all scripts: * `option_renamed(oldname, newname)`, indicates that an option has been renamed @@ -200,11 +188,11 @@ Others migration functions -------------------------- -Those functions are only used for low level operations that could not be -accomplished otherwise or to repair damaged databases during interactive +Those functions are only used for low level operations that could not be +accomplished otherwise or to repair damaged databases during interactive session. They are available in `repository` scripts: -* `sqlexec(sql, args=None, ask_confirm=True)`, executes an arbitrary SQL query +* `sql(sql, args=None, ask_confirm=True)`, executes an arbitrary SQL query on the system source * `add_entity_type_table(etype, commit=True)` * `add_relation_type_table(rtype, commit=True)` * `uninline_relation(rtype, commit=True)` diff -r 9e639925ca2f -r ff6114c2c416 doc/book/en/development/profiling/index.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/development/profiling/index.rst Tue Aug 04 15:06:09 2009 +0200 @@ -0,0 +1,55 @@ +Profiling and performance +========================= + +If you feel that one of your pages takes more time than it should to be +generated, chances are that you're making too many RQL queries. Obviously, +there are other reasons but experience tends to show this is the first thing to +track down. Luckily, CubicWeb provides a configuration option to log RQL +queries. In your ``all-in-one.conf`` file, set the **query-log-file** option:: + + # web application query log file + query-log-file=~/myapp-rql.log + +Then restart your application, reload your page and stop your application. +The file ``myapp-rql.log`` now contains the list of RQL queries that were +executed during your test. It's a simple text file containing lines such as:: + + Any A WHERE X eid %(x)s, X lastname A {'x': 448} -- (0.002 sec, 0.010 CPU sec) + Any A WHERE X eid %(x)s, X firstname A {'x': 447} -- (0.002 sec, 0.000 CPU sec) + +The structure of each line is:: + + --