# HG changeset patch # User Nicolas Chauvat # Date 1244050943 -7200 # Node ID 85eae6a79f75c8b6bd728d695584f2daa6a95762 # Parent a81d3babb5825e9ddd96f326ceadc9f65adb5485# Parent 74edd1b34c52e31b8adcb5dd7967d6e24ed39dfc close treeviews-tabs branch by merging with 3.1 diff -r a81d3babb582 -r 85eae6a79f75 .hgtags --- a/.hgtags Thu Jan 15 10:13:25 2009 +0100 +++ b/.hgtags Wed Jun 03 19:42:23 2009 +0200 @@ -12,3 +12,7 @@ 18d3e56c1de4a6597ab36536964bc66b5550cf98 cubicweb-debian-version-3_0_2-1 0cb027c056f939ec3580ea1cc0aeda9f9884f0fa cubicweb-version-3_0_3 a736bae56d4a703a26933fb9875fb9caab216c6b cubicweb-debian-version-3_0_3-1 +2e400b8dfc25ae30db602f64601e30e210b3fade cubicweb-version-3_0_4 +fc222bc99929d395c1c2235c40d3bb6f247b4ba9 cubicweb-debian-version-3_0_4-1 +7ad527099393ef56f27af313392022bb8ed73082 cubicweb-version-3_0_9 +a8e9e53b245d53838a07aa8c76d1bed352692a9f cubicweb-debian-version-3_0_9-1 diff -r a81d3babb582 -r 85eae6a79f75 __pkginfo__.py --- a/__pkginfo__.py Thu Jan 15 10:13:25 2009 +0100 +++ b/__pkginfo__.py Wed Jun 03 19:42:23 2009 +0200 @@ -6,11 +6,11 @@ distname = "cubicweb" modname = "cubicweb" -numversion = (3, 0, 3) +numversion = (3, 0, 10) version = '.'.join(str(num) for num in numversion) -license = 'LCL' -copyright = '''Copyright (c) 2003-2008 LOGILAB S.A. (Paris, FRANCE). +license = 'GPL' +copyright = '''Copyright (c) 2003-2009 LOGILAB S.A. (Paris, FRANCE). http://www.logilab.fr/ -- mailto:contact@logilab.fr''' author = "Logilab" @@ -27,11 +27,12 @@ * a bunch of other management tools """ -web = '' -ftp = '' -pyversions = ['2.4'] +web = 'http://www.cubicweb.org' +ftp = 'ftp://ftp.logilab.org/pub/cubicweb' +pyversions = ['2.4', '2.5'] +import sys from os import listdir, environ from os.path import join, isdir import glob @@ -60,7 +61,8 @@ # --home install pydir = 'python' else: - pydir = join('python2.4', 'site-packages') + python_version = '.'.join(str(num) for num in sys.version_info[0:2]) + pydir = join('python' + python_version, 'site-packages') try: data_files = [ @@ -90,7 +92,6 @@ [join('share', 'cubicweb', 'cubes', 'shared', 'i18n'), [join(i18n_dir, fname) for fname in listdir(i18n_dir)]], # skeleton - ] except OSError: # we are in an installed directory, don't care about this diff -r a81d3babb582 -r 85eae6a79f75 common/appobject.py --- a/common/appobject.py Thu Jan 15 10:13:25 2009 +0100 +++ b/common/appobject.py Wed Jun 03 19:42:23 2009 +0200 @@ -1,7 +1,7 @@ """Base class for dynamically loaded objects manipulated in the web interface :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -12,6 +12,8 @@ from simplejson import dumps from logilab.common.deprecation import obsolete + +from rql.nodes import VariableRef, SubQuery from rql.stmts import Union, Select from cubicweb import Unauthorized diff -r a81d3babb582 -r 85eae6a79f75 common/entity.py --- a/common/entity.py Thu Jan 15 10:13:25 2009 +0100 +++ b/common/entity.py Wed Jun 03 19:42:23 2009 +0200 @@ -1,7 +1,7 @@ """Base class for entity objects manipulated in clients :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -391,6 +391,10 @@ res['source'] = self.req.source_defs()[res['source']] return res + def clear_local_perm_cache(self, action): + for rqlexpr in self.e_schema.get_rqlexprs(action): + self.req.local_perm_cache.pop((rqlexpr.eid, (('x', self.eid),)), None) + def check_perm(self, action): self.e_schema.check_perm(self.req, action, self.eid) @@ -998,6 +1002,7 @@ _done.add(self.eid) containers = tuple(self.e_schema.fulltext_containers()) if containers: + yielded = False for rschema, target in containers: if target == 'object': targets = getattr(self, rschema.type) @@ -1008,6 +1013,9 @@ continue for container in entity.fti_containers(_done): yield container + yielded = True + if not yielded: + yield self else: yield self diff -r a81d3babb582 -r 85eae6a79f75 common/migration.py --- a/common/migration.py Thu Jan 15 10:13:25 2009 +0100 +++ b/common/migration.py Wed Jun 03 19:42:23 2009 +0200 @@ -312,12 +312,18 @@ """a configuration option's type has changed""" self._option_changes.append(('typechanged', optname, oldtype, newvalue)) - def cmd_add_cube(self, cube): + def cmd_add_cubes(self, cubes): + """modify the list of used cubes in the in-memory config + returns newly inserted cubes, including dependencies + """ + if isinstance(cubes, basestring): + cubes = (cubes,) origcubes = self.config.cubes() - newcubes = [p for p in self.config.expand_cubes([cube]) + newcubes = [p for p in self.config.expand_cubes(cubes) if not p in origcubes] if newcubes: - assert cube in newcubes + for cube in cubes: + assert cube in newcubes self.config.add_cubes(newcubes) return newcubes @@ -346,7 +352,7 @@ if optdescr[0] == 'added': optdict = self.config.get_option_def(optdescr[1]) if optdict.get('default') is REQUIRED: - self.config.input_option(option, optdict) + self.config.input_option(optdescr[1], optdict) self.config.generate_config(open(newconfig, 'w')) show_diffs(configfile, newconfig) if exists(newconfig): diff -r a81d3babb582 -r 85eae6a79f75 common/mixins.py --- a/common/mixins.py Thu Jan 15 10:13:25 2009 +0100 +++ b/common/mixins.py Wed Jun 03 19:42:23 2009 +0200 @@ -145,7 +145,6 @@ return self.iterchildren() def is_leaf(self): - print '*' * 80 return len(self.children()) == 0 def is_root(self): @@ -165,7 +164,11 @@ @property def state(self): - return self.in_state[0].name + try: + return self.in_state[0].name + except IndexError: + self.warning('entity %s has no state', self) + return None @property def displayable_state(self): diff -r a81d3babb582 -r 85eae6a79f75 common/selectors.py --- a/common/selectors.py Thu Jan 15 10:13:25 2009 +0100 +++ b/common/selectors.py Wed Jun 03 19:42:23 2009 +0200 @@ -20,12 +20,12 @@ above by:: # in Python2.5 - from cubicweb.selectors import traced_selection + from cubicweb.common.selectors import traced_selection with traced_selection(): self.view('calendar', myrset) # in Python2.4 - from cubicweb import selectors + from cubicweb.common import selectors selectors.TRACED_OIDS = ('calendar',) self.view('calendar', myrset) selectors.TRACED_OIDS = () @@ -50,7 +50,6 @@ from cubicweb.cwconfig import CubicWebConfiguration from cubicweb.schema import split_expression - # helpers for debugging selectors SELECTOR_LOGGER = logging.getLogger('cubicweb.selectors') TRACED_OIDS = () @@ -64,6 +63,7 @@ if TRACED_OIDS == 'all' or cls.id in TRACED_OIDS: SELECTOR_LOGGER.warning('selector %s returned %s for %s', selector.__name__, ret, cls) return ret + traced.__name__ = selector.__name__ return traced class traced_selection(object): @@ -110,12 +110,12 @@ norset_selector = deprecated_function(none_rset) @lltrace -def rset(cls, req, rset, *args, **kwargs): +def any_rset(cls, req, rset, *args, **kwargs): """accept result set, whatever the number of result""" if rset is not None: return 1 return 0 -rset_selector = deprecated_function(rset) +rset_selector = deprecated_function(any_rset) @lltrace def nonempty_rset(cls, req, rset, *args, **kwargs): @@ -161,13 +161,20 @@ def paginated_rset(cls, req, rset, *args, **kwargs): """accept result sets with more rows than the page size """ - if rset is None or len(rset) <= req.property_value('navigation.page-size'): + page_size = kwargs.get('page_size') + if page_size is None: + page_size = req.form.get('page_size') + if page_size is None: + page_size = req.property_value('navigation.page-size') + else: + page_size = int(page_size) + if rset is None or len(rset) <= page_size: return 0 return 1 largerset_selector = deprecated_function(paginated_rset) @lltrace -def sorted_rset(cls, req, rset, row=None, col=None): +def sorted_rset(cls, req, rset, row=None, col=None, **kwargs): """accept sorted result set""" rqlst = rset.syntax_tree() if len(rqlst.children) > 1 or not rqlst.children[0].orderby: @@ -222,7 +229,7 @@ @lltrace def authenticated_user(cls, req, *args, **kwargs): """accept if user is authenticated""" - return not anonymous_selector(cls, req, *args, **kwargs) + return not anonymous_user(cls, req, *args, **kwargs) not_anonymous_selector = deprecated_function(authenticated_user) @lltrace @@ -499,7 +506,7 @@ propval = req.property_value('%s.%s.context' % (cls.__registry__, cls.id)) if not propval: propval = cls.context - if context is not None and propval is not None and context != propval: + if context is not None and propval and context != propval: return 0 return 1 contextprop_selector = deprecated_function(match_context_prop) @@ -529,28 +536,36 @@ # compound selectors ########################################################## non_final_entity = chainall(nonempty_rset, _non_final_entity) +non_final_entity.__name__ = 'non_final_entity' nfentity_selector = deprecated_function(non_final_entity) implement_interface = chainall(non_final_entity, _implement_interface) +implement_interface.__name__ = 'implement_interface' interface_selector = deprecated_function(implement_interface) accept = chainall(non_final_entity, accept_rset) +accept.__name__ = 'accept' accept_selector = deprecated_function(accept) -accept_one = chainall(one_line_rset, accept_selector) +accept_one = chainall(one_line_rset, accept) +accept_one.__name__ = 'accept_one' accept_one_selector = deprecated_function(accept_one) rql_condition = chainall(non_final_entity, one_line_rset, _rql_condition) +rql_condition.__name__ = 'rql_condition' rqlcondition_selector = deprecated_function(rql_condition) searchstate_accept = chainall(nonempty_rset, match_search_state, accept) +searchstate_accept.__name__ = 'searchstate_accept' searchstate_accept_selector = deprecated_function(searchstate_accept) searchstate_accept_one = chainall(one_line_rset, match_search_state, accept, _rql_condition) +searchstate_accept_one.__name__ = 'searchstate_accept_one' searchstate_accept_one_selector = deprecated_function(searchstate_accept_one) searchstate_accept_one_but_etype = chainall(searchstate_accept_one, but_etype) +searchstate_accept_one_but_etype.__name__ = 'searchstate_accept_one_but_etype' searchstate_accept_one_but_etype_selector = deprecated_function( searchstate_accept_one_but_etype) diff -r a81d3babb582 -r 85eae6a79f75 common/test/unittest_entity.py --- a/common/test/unittest_entity.py Thu Jan 15 10:13:25 2009 +0100 +++ b/common/test/unittest_entity.py Wed Jun 03 19:42:23 2009 +0200 @@ -250,8 +250,7 @@ self.assertListEquals(rbc(e.relations_by_category('generic')), [('primary_email', 'subject'), ('evaluee', 'subject'), - ('for_user', 'object'), - ('bookmarked_by', 'object')]) + ('for_user', 'object')]) # owned_by is defined both as subject and object relations on EUser self.assertListEquals(rbc(e.relations_by_category('generated')), [('last_login_time', 'subject'), @@ -263,7 +262,8 @@ ('owned_by', 'subject'), ('created_by', 'object'), ('wf_info_for', 'object'), - ('owned_by', 'object')]) + ('owned_by', 'object'), + ('bookmarked_by', 'object')]) e = self.etype_instance('Personne') self.assertListEquals(rbc(e.relations_by_category('primary')), [('nom', 'subject'), ('eid', 'subject')]) diff -r a81d3babb582 -r 85eae6a79f75 common/uilib.py --- a/common/uilib.py Thu Jan 15 10:13:25 2009 +0100 +++ b/common/uilib.py Wed Jun 03 19:42:23 2009 +0200 @@ -4,7 +4,7 @@ contains some functions designed to help implementation of cubicweb user interface :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -213,6 +213,21 @@ # HTML generation helper functions ############################################ +def simple_sgml_tag(tag, content=None, **attrs): + """generation of a simple sgml tag (eg without children tags) easier + + content and attributes will be escaped + """ + value = u'<%s' % tag + if attrs: + value += u' ' + u' '.join(u'%s="%s"' % (attr, html_escape(unicode(value))) + for attr, value in attrs.items()) + if content: + value += u'>%s' % (html_escape(unicode(content)), tag) + else: + value += u'/>' + return value + def tooltipize(text, tooltip, url=None): """make an HTML tooltip""" url = url or '#' diff -r a81d3babb582 -r 85eae6a79f75 common/view.py --- a/common/view.py Thu Jan 15 10:13:25 2009 +0100 +++ b/common/view.py Wed Jun 03 19:42:23 2009 +0200 @@ -2,7 +2,7 @@ :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -28,9 +28,9 @@ ] ''' TRANSITIONAL_DOCTYPE = u'\n' @@ -71,7 +73,7 @@ A view is instantiated to render a [part of a] result set. View subclasses may be parametred using the following class attributes: - + * `templatable` indicates if the view may be embeded in a main template or if it has to be rendered standalone (i.e. XML for instance) @@ -85,14 +87,14 @@ time to a write function to use. """ __registry__ = 'views' - + templatable = True need_navigation = True # content_type = 'application/xhtml+xml' # text/xhtml' binary = False add_to_breadcrumbs = True category = 'view' - + def __init__(self, req, rset): super(View, self).__init__(req, rset) self.w = None @@ -102,7 +104,7 @@ if self.req.xhtml_browser(): return 'application/xhtml+xml' return 'text/html' - + def set_stream(self, w=None): if self.w is not None: return @@ -118,14 +120,14 @@ return stream # main view interface ##################################################### - + def dispatch(self, w=None, **context): """called to render a view object for a result set. This method is a dispatched to an actual method selected according to optional row and col parameters, which are locating a particular row or cell in the result set: - + * if row [and col] are specified, `cell_call` is called * if none of them is supplied, the view is considered to apply on the whole result set (which may be None in this case), `call` is @@ -147,7 +149,7 @@ # should default .call() method add a
around each # rset item add_div_section = True - + def call(self, **kwargs): """the view is called for an entire result set, by default loop other rows of the result set and call the same view on the @@ -169,10 +171,10 @@ def cell_call(self, row, col, **kwargs): """the view is called for a particular result set cell""" raise NotImplementedError, self - + def linkable(self): """return True if the view may be linked in a menu - + by default views without title are not meant to be displayed """ if not getattr(self, 'title', None): @@ -181,7 +183,7 @@ def is_primary(self): return self.id == 'primary' - + def url(self): """return the url associated with this view. Should not be necessary for non linkable views, but a default implementation @@ -197,7 +199,7 @@ self.req.set_content_type(self.content_type) # view utilities ########################################################## - + def view(self, __vid, rset, __fallback_vid=None, **kwargs): """shortcut to self.vreg.render method avoiding to pass self.req""" try: @@ -207,7 +209,7 @@ raise view = self.vreg.select_view(__fallback_vid, self.req, rset, **kwargs) return view.dispatch(**kwargs) - + def wview(self, __vid, rset, __fallback_vid=None, **kwargs): """shortcut to self.view method automatically passing self.w as argument """ @@ -234,7 +236,7 @@ label = label or self.req._(action.title) return u'%s' % (html_escape(action.url()), label) return u'' - + def html_headers(self): """return a list of html headers (eg something to be inserted between and of the returned page @@ -242,7 +244,7 @@ by default return a meta tag to disable robot indexation of the page """ return [NOINDEX] - + def page_title(self): """returns a title according to the result set - used for the title in the HTML header @@ -292,7 +294,7 @@ """ return the url of the entity creation form for a given entity type""" return self.req.build_url('add/%s'%etype, **kwargs) - + # concrete views base classes ################################################# class EntityView(View): @@ -301,7 +303,7 @@ __registerer__ = accepts_registerer __selectors__ = (accept,) category = 'entityview' - + def field(self, label, value, row=True, show_label=True, w=None, tr=True): """ read-only field """ if w is None: @@ -316,7 +318,7 @@ if row: w(u'
') - + class StartupView(View): """base class for views which doesn't need a particular result set to be displayed (so they can always be displayed !) @@ -325,7 +327,7 @@ __selectors__ = (match_user_group, none_rset) require_groups = () category = 'startupview' - + def url(self): """return the url associated with this view. We can omit rql here""" return self.build_url('view', vid=self.id) @@ -345,9 +347,9 @@ """ __registerer__ = accepts_registerer __selectors__ = (chainfirst(none_rset, accept),) - + default_rql = None - + def __init__(self, req, rset): super(EntityStartupView, self).__init__(req, rset) if rset is None: @@ -357,7 +359,7 @@ def startup_rql(self): """return some rql to be executedif the result set is None""" return self.default_rql - + def call(self, **kwargs): """override call to execute rql returned by the .startup_rql method if necessary @@ -376,14 +378,14 @@ return self.build_url(vid=self.id) return super(EntityStartupView, self).url() - + class AnyRsetView(View): """base class for views applying on any non empty result sets""" __registerer__ = priority_registerer __selectors__ = (nonempty_rset,) - + category = 'anyrsetview' - + def columns_labels(self, tr=True): if tr: translate = display_name @@ -400,7 +402,7 @@ label = translate(self.req, attr) labels.append(label) return labels - + class EmptyRsetView(View): """base class for views applying on any empty result sets""" @@ -419,7 +421,7 @@ __selectors__ = (match_user_group,) require_groups = () - + def template(self, oid, **kwargs): """shortcut to self.registry.render method on the templates registry""" w = kwargs.pop('w', self.w) diff -r a81d3babb582 -r 85eae6a79f75 cwconfig.py --- a/cwconfig.py Thu Jan 15 10:13:25 2009 +0100 +++ b/cwconfig.py Wed Jun 03 19:42:23 2009 +0200 @@ -148,6 +148,8 @@ if os.environ.get('APYCOT_ROOT'): mode = 'test' CUBES_DIR = '%(APYCOT_ROOT)s/local/share/cubicweb/cubes/' % os.environ + # create __init__ file + file(join(CUBES_DIR, '__init__.py'), 'w').close() elif exists(join(CW_SOFTWARE_ROOT, '.hg')): mode = 'dev' CUBES_DIR = join(CW_SOFTWARE_ROOT, '../cubes') diff -r a81d3babb582 -r 85eae6a79f75 debian/changelog --- a/debian/changelog Thu Jan 15 10:13:25 2009 +0100 +++ b/debian/changelog Wed Jun 03 19:42:23 2009 +0200 @@ -1,3 +1,22 @@ +cubicweb (3.0.10-1) unstable; urgency=low + + * merge cubicweb-core package into cubicweb-common + * simplify debian/rules + + -- Julien Jehannet Thu, 19 Feb 2009 16:24:09 +0100 + +cubicweb (3.0.9-1) unstable; urgency=low + + * new upstream (interim) release + + -- Aurélien Campéas Tue, 10 Feb 2009 14:05:12 +0100 + +cubicweb (3.0.4-1) unstable; urgency=low + + * new upstream release + + -- Sylvain Thénault Tue, 27 Jan 2009 16:05:12 +0100 + cubicweb (3.0.3-1) DISTRIBUTION; urgency=low * new upstream release diff -r a81d3babb582 -r 85eae6a79f75 debian/compat --- a/debian/compat Thu Jan 15 10:13:25 2009 +0100 +++ b/debian/compat Wed Jun 03 19:42:23 2009 +0200 @@ -1,1 +1,1 @@ -5 +7 diff -r a81d3babb582 -r 85eae6a79f75 debian/control --- a/debian/control Thu Jan 15 10:13:25 2009 +0100 +++ b/debian/control Wed Jun 03 19:42:23 2009 +0200 @@ -2,16 +2,19 @@ Section: web Priority: optional Maintainer: Logilab Packaging Team -Uploaders: Sylvain Thenault -Build-Depends: debhelper (>= 5.0.37.1), python (>=2.4), python-dev (>=2.4), python-central (>= 0.5) +Uploaders: Sylvain Thenault , + Julien Jehannet +Build-Depends: debhelper (>= 7), python-dev (>=2.4), python-central (>= 0.5) Standards-Version: 3.8.0 +Homepage: http://www.cubicweb.org XS-Python-Version: >= 2.4, << 2.6 + Package: cubicweb Architecture: all XB-Python-Version: ${python:Versions} Depends: ${python:Depends}, cubicweb-server (= ${source:Version}), cubicweb-twisted (= ${source:Version}), cubicweb-client (= ${source:Version}) -XBS-Recommends: (postgresql, postgresql-plpython, postgresql-contrib) | mysql | sqlite3 +XB-Recommends: (postgresql, postgresql-plpython, postgresql-contrib) | mysql | sqlite3 Recommends: postgresql | mysql | sqlite3 Description: the complete CubicWeb framework CubicWeb is a semantic web application framework. @@ -72,8 +75,10 @@ Package: cubicweb-common Architecture: all XB-Python-Version: ${python:Versions} -Depends: ${python:Depends}, cubicweb-core (= ${source:Version}), python-logilab-mtconverter (>= 0.4.0), python-simpletal (>= 4.0), graphviz, gettext, python-lxml +Depends: ${python:Depends}, python-logilab-mtconverter (>= 0.4.0), python-simpletal (>= 4.0), graphviz, gettext, python-lxml, python-logilab-common (>= 0.37.2), python-yams (>= 0.20.2), python-rql (>= 0.20.2) Recommends: python-psyco +Conflicts: cubicweb-core +Replaces: cubicweb-core Description: common library for the CubicWeb framework CubicWeb is a semantic web application framework. . @@ -84,7 +89,7 @@ Package: cubicweb-ctl Architecture: all XB-Python-Version: ${python:Versions} -Depends: ${python:Depends}, cubicweb-core (= ${source:Version}) +Depends: ${python:Depends}, cubicweb-common (= ${source:Version}) Description: tool to manage the CubicWeb framework CubicWeb is a semantic web application framework. . @@ -104,17 +109,6 @@ pyro to connect to a repository server. -Package: cubicweb-core -Architecture: all -XB-Python-Version: ${python:Versions} -Depends: ${python:Depends}, python-logilab-common (>= 0.37.2), python-yams (>= 0.20.2), python-rql (>= 0.20.2) -Description: core library for the CubicWeb framework - CubicWeb is a semantic web application framework. - . - This package provides the core part of the library used by anyone having - to do some cubicweb programming in Python. - - Package: cubicweb-dev Architecture: all XB-Python-Version: ${python:Versions} diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-client.dirs --- a/debian/cubicweb-client.dirs Thu Jan 15 10:13:25 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -usr/lib/python2.4/site-packages/cubicweb/ diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-client.install.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/cubicweb-client.install.in Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,1 @@ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/hercule.py usr/lib/PY_VERSION/site-packages/cubicweb diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-common.dirs --- a/debian/cubicweb-common.dirs Thu Jan 15 10:13:25 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -usr/lib/python2.4/site-packages/cubicweb -usr/lib/python2.4/site-packages/cubicweb/common -usr/share/cubicweb/cubes/shared -usr/share/doc/cubicweb-common diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-common.install.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/cubicweb-common.install.in Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,17 @@ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/common/ usr/lib/PY_VERSION/site-packages/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/entities/ usr/lib/PY_VERSION/site-packages/cubicweb +debian/tmp/usr/share/cubicweb/cubes/shared/i18n usr/share/cubicweb/cubes/shared/ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/rset.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/gettext.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/toolsutils.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/cwvreg.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/_exceptions.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/schemaviewer.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/dbapi.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/cwconfig.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/__init__.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/md5crypt.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/schema.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/interfaces.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/vregistry.py usr/share/pyshared/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/__pkginfo__.py usr/share/pyshared/cubicweb diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-core.dirs --- a/debian/cubicweb-core.dirs Thu Jan 15 10:13:25 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -usr/lib/python2.4/site-packages/cubicweb -usr/share/doc/cubicweb-core diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-ctl.cubicweb.init --- a/debian/cubicweb-ctl.cubicweb.init Thu Jan 15 10:13:25 2009 +0100 +++ b/debian/cubicweb-ctl.cubicweb.init Wed Jun 03 19:42:23 2009 +0200 @@ -11,5 +11,22 @@ # Short-Description: Start cubicweb application at boot time ### END INIT INFO +# FIXME Seems to be inadequate here +# FIXME If related to pyro, try instead: +# export PYRO_STORAGE="/tmp" cd /tmp -/usr/bin/cubicweb-ctl $1 --force + +# FIXME Work-around about the following lintian error +# E: cubicweb-ctl: init.d-script-does-not-implement-required-option /etc/init.d/cubicweb start +# +# Check if we are sure to not want the start-stop-daemon machinery here +# Refer to Debian Policy Manual section 9.3.2 (Writing the scripts) for details. + +case "$1" in + "force-reload") + /usr/bin/cubicweb-ctl reload --force + ;; + "*|restart") + /usr/bin/cubicweb-ctl $1 --force + ;; +esac diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-ctl.install.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/cubicweb-ctl.install.in Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,3 @@ +debian/tmp/usr/bin/cubicweb-ctl usr/bin/ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/cwctl.py usr/lib/PY_VERSION/site-packages/cubicweb +debian/cubicweb-ctl.bash_completion etc/bash_completion.d/cubicweb-ctl diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-ctl.postinst --- a/debian/cubicweb-ctl.postinst Thu Jan 15 10:13:25 2009 +0100 +++ b/debian/cubicweb-ctl.postinst Wed Jun 03 19:42:23 2009 +0200 @@ -2,7 +2,7 @@ case "$1" in configure|abort-upgrade|abort-remove|abort-deconfigure) - update-rc.d cubicweb defaults >/dev/null + update-rc.d cubicweb defaults 99 >/dev/null ;; *) echo "postinst called with unknown argument \`$1'" >&2 diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-dev.dirs --- a/debian/cubicweb-dev.dirs Thu Jan 15 10:13:25 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -usr/lib/python2.4/site-packages/cubicweb -usr/lib/python2.4/site-packages/cubicweb/common -usr/lib/python2.4/site-packages/cubicweb/web -usr/lib/python2.4/site-packages/cubicweb/server -usr/lib/python2.4/site-packages/cubicweb/sobjects -usr/lib/python2.4/site-packages/cubicweb/entities -usr/share/doc/cubicweb-dev diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-dev.install.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/cubicweb-dev.install.in Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,7 @@ +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/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/ diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-documentation.dirs --- a/debian/cubicweb-documentation.dirs Thu Jan 15 10:13:25 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -usr/share/doc/cubicweb-documentation/ -usr/share/doc/cubicweb-documentation/devmanual_fr -usr/share/doc-base/ diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-documentation.install --- a/debian/cubicweb-documentation.install Thu Jan 15 10:13:25 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -debian/cubicweb-doc usr/share/doc-base/ diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-documentation.install.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/cubicweb-documentation.install.in Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,2 @@ +doc/book usr/share/doc/cubicweb-documentation +debian/cubicweb-doc usr/share/doc-base/cubicweb-doc diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-server.dirs --- a/debian/cubicweb-server.dirs Thu Jan 15 10:13:25 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -usr/lib/python2.4/site-packages/cubicweb/ -usr/share/cubicweb -usr/share/doc/cubicweb-server diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-server.install.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/cubicweb-server.install.in Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,4 @@ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/server/ usr/lib/PY_VERSION/site-packages/cubicweb +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/sobjects/ usr/lib/PY_VERSION/site-packages/cubicweb +debian/tmp/usr/share/cubicweb/schemas/ usr/share/cubicweb/ +debian/tmp/usr/share/cubicweb/migration/ usr/share/cubicweb/ diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-twisted.dirs --- a/debian/cubicweb-twisted.dirs Thu Jan 15 10:13:25 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -usr/lib/python2.4/site-packages -usr/lib/python2.4/site-packages/cubicweb -usr/share/doc/cubicweb-twisted diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-twisted.install.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/cubicweb-twisted.install.in Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,1 @@ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/etwist/ usr/lib/PY_VERSION/site-packages/cubicweb/ diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-web.dirs --- a/debian/cubicweb-web.dirs Thu Jan 15 10:13:25 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -usr/lib/python2.4/site-packages/ -usr/lib/python2.4/site-packages/cubicweb -usr/share/cubicweb/cubes/shared -usr/share/doc/cubicweb-web diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-web.install.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/cubicweb-web.install.in Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,3 @@ +debian/tmp/usr/lib/PY_VERSION/site-packages/cubicweb/web usr/lib/PY_VERSION/site-packages/cubicweb +debian/tmp/usr/share/cubicweb/cubes/shared/data usr/share/cubicweb/cubes/shared +debian/tmp/usr/share/cubicweb/cubes/shared/wdoc usr/share/cubicweb/cubes/shared diff -r a81d3babb582 -r 85eae6a79f75 debian/cubicweb-web.lintian-overrides --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/debian/cubicweb-web.lintian-overrides Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,1 @@ +cubicweb-web: embedded-javascript-library usr/share/cubicweb/cubes/shared/data/jquery.js diff -r a81d3babb582 -r 85eae6a79f75 debian/rules --- a/debian/rules Thu Jan 15 10:13:25 2009 +0100 +++ b/debian/rules Wed Jun 03 19:42:23 2009 +0200 @@ -4,12 +4,16 @@ # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 + +PY_VERSION:=$(shell pyversions -d) + build: build-stamp build-stamp: dh_testdir # XXX doesn't work if logilab-doctools, logilab-xml are not in build depends # and I can't get pbuilder find them in its chroot :( - #cd doc && make + # cd doc && make + # FIXME cleanup and use sphinx-build as build-depends ? python setup.py build touch build-stamp @@ -18,78 +22,42 @@ dh_testroot rm -f build-stamp configure-stamp rm -rf build - rm -rf debian/cubicweb-*/ - find . -name "*.pyc" | xargs rm -f + #rm -rf debian/cubicweb-*/ + find . -name "*.pyc" -delete + rm -f $(basename $(wildcard debian/*.in)) dh_clean -install: build +install: build $(basename $(wildcard debian/*.in)) dh_testdir dh_testroot - dh_clean -k + dh_clean dh_installdirs - ########## core package ############################################# - # put : - # * all the python library and data in cubicweb-core - # * scripts in cubicweb-server - # - # pick from each latter to construct each package - python setup.py -q install_lib --no-compile --install-dir=debian/cubicweb-core/usr/lib/python2.4/site-packages/ - python setup.py -q install_data --install-dir=debian/cubicweb-core/usr/ - python setup.py -q install_scripts --install-dir=debian/cubicweb-server/usr/bin/ - ########## common package ############################################# - mv debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/common/ debian/cubicweb-common/usr/lib/python2.4/site-packages/cubicweb - mv debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/entities/ debian/cubicweb-common/usr/lib/python2.4/site-packages/cubicweb - # data - mv debian/cubicweb-core/usr/share/cubicweb/cubes/shared/i18n debian/cubicweb-common/usr/share/cubicweb/cubes/shared/ + + #python setup.py install_lib --no-compile --install-dir=debian/cubicweb-common/usr/lib/python2.4/site-packages/ + python setup.py -q install --no-compile --prefix=debian/tmp/usr + + # Put all the python library and data in cubicweb-common + # and scripts in cubicweb-server + dh_install -vi + dh_lintian + + # Remove unittests directory (should be available in cubicweb-dev only) + 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-common/usr/lib/${PY_VERSION}/site-packages/cubicweb/common/test + + # cubes directory must be managed as a valid python module + ls -l debian/cubicweb-common/usr/share/cubicweb/cubes touch debian/cubicweb-common/usr/share/cubicweb/cubes/__init__.py - ########## server package ############################################# - # library - mv debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/server/ debian/cubicweb-server/usr/lib/python2.4/site-packages/cubicweb - mv debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/sobjects/ debian/cubicweb-server/usr/lib/python2.4/site-packages/cubicweb - # data - mv debian/cubicweb-core/usr/share/cubicweb/schemas/ debian/cubicweb-server/usr/share/cubicweb/ - mv debian/cubicweb-core/usr/share/cubicweb/migration/ debian/cubicweb-server/usr/share/cubicweb/ - ########## twisted package ############################################ - # library - mv debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/etwist/ debian/cubicweb-twisted/usr/lib/python2.4/site-packages/cubicweb/ - ########## web package ################################################ - # library - mv debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/web/ debian/cubicweb-web/usr/lib/python2.4/site-packages/cubicweb/ - # data / web documentation - mv debian/cubicweb-core/usr/share/cubicweb/cubes/shared/data debian/cubicweb-web/usr/share/cubicweb/cubes/shared/ - mv debian/cubicweb-core/usr/share/cubicweb/cubes/shared/wdoc debian/cubicweb-web/usr/share/cubicweb/cubes/shared/ - ########## ctl package ################################################ - # scripts - mv debian/cubicweb-server/usr/bin/cubicweb-ctl debian/cubicweb-ctl/usr/bin/ - mv debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/cwctl.py debian/cubicweb-ctl/usr/lib/python2.4/site-packages/cubicweb - mv debian/cubicweb-ctl.bash_completion debian/cubicweb-ctl/etc/bash_completion.d/cubicweb-ctl - ########## client package ############################################# - # library - mv debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/hercule.py debian/cubicweb-client/usr/lib/python2.4/site-packages/cubicweb - ########## dev package ################################################ - # devtools package - mv debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/devtools/ debian/cubicweb-dev/usr/lib/python2.4/site-packages/cubicweb/ - mv debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/skeleton/ debian/cubicweb-dev/usr/lib/python2.4/site-packages/cubicweb/ - # tests directories - mv debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/test debian/cubicweb-dev/usr/lib/python2.4/site-packages/cubicweb/ - mv debian/cubicweb-common/usr/lib/python2.4/site-packages/cubicweb/common/test debian/cubicweb-dev/usr/lib/python2.4/site-packages/cubicweb/common/ - mv debian/cubicweb-server/usr/lib/python2.4/site-packages/cubicweb/server/test debian/cubicweb-dev/usr/lib/python2.4/site-packages/cubicweb/server/ - mv debian/cubicweb-server/usr/lib/python2.4/site-packages/cubicweb/sobjects/test debian/cubicweb-dev/usr/lib/python2.4/site-packages/cubicweb/sobjects/ - mv debian/cubicweb-web/usr/lib/python2.4/site-packages/cubicweb/web/test debian/cubicweb-dev/usr/lib/python2.4/site-packages/cubicweb/web/ - ########## documentation package ###################################### - cp -r doc/book debian/cubicweb-documentation/usr/share/doc/cubicweb-documentation/ - ########## core package ############################################### - # small cleanup - rm -rf debian/cubicweb-core/usr/share/cubicweb/ - # undistributed for now - rm -rf debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/goa - rm -rf debian/cubicweb-core/usr/lib/python2.4/site-packages/cubicweb/wsgi + +%: %.in + sed "s/PY_VERSION/${PY_VERSION}/g" < $< > $@ # Build architecture-independent files here. binary-indep: build install dh_testdir dh_testroot -i - dh_install -i dh_pycentral -i dh_installinit -i -n --name cubicweb -u"defaults 99" dh_installlogrotate -i diff -r a81d3babb582 -r 85eae6a79f75 devtools/fill.py --- a/devtools/fill.py Thu Jan 15 10:13:25 2009 +0100 +++ b/devtools/fill.py Wed Jun 03 19:42:23 2009 +0200 @@ -136,6 +136,9 @@ def generate_integer(self, attrname, index): """generates a consistent value for 'attrname' if it's an integer""" + choosed = self.generate_choice(attrname, index) + if choosed is not None: + return choosed minvalue, maxvalue = get_bounds(self.e_schema, attrname) if maxvalue is not None and maxvalue <= 0 and minvalue is None: minvalue = maxvalue - index # i.e. randint(-index, 0) diff -r a81d3babb582 -r 85eae6a79f75 devtools/repotest.py --- a/devtools/repotest.py Thu Jan 15 10:13:25 2009 +0100 +++ b/devtools/repotest.py Wed Jun 03 19:42:23 2009 +0200 @@ -3,7 +3,7 @@ This module contains functions to initialize a new repository. :organization: Logilab -:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -249,7 +249,8 @@ _orig_select_principal = rqlannotation._select_principal def _select_principal(scope, relations): - return _orig_select_principal(scope, sorted(relations, key=lambda x: x.r_type)) + return _orig_select_principal(scope, relations, + _sort=lambda rels: sorted(rels, key=lambda x: x.r_type)) try: from cubicweb.server.msplanner import PartPlanInformation diff -r a81d3babb582 -r 85eae6a79f75 devtools/test/data/bootstrap_cubes --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/devtools/test/data/bootstrap_cubes Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,1 @@ +person, comment diff -r a81d3babb582 -r 85eae6a79f75 devtools/test/data/bootstrap_packages --- a/devtools/test/data/bootstrap_packages Thu Jan 15 10:13:25 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -eperson, ecomment diff -r a81d3babb582 -r 85eae6a79f75 devtools/testlib.py --- a/devtools/testlib.py Thu Jan 15 10:13:25 2009 +0100 +++ b/devtools/testlib.py Wed Jun 03 19:42:23 2009 +0200 @@ -78,6 +78,8 @@ return center - before <= line_no <= center + after ## base webtest class ######################################################### +VALMAP = {None: None, 'dtd': DTDValidator, 'xml': SaxOnlyValidator} + class WebTest(EnvBasedTC): """base class for web tests""" __abstract__ = True @@ -93,35 +95,28 @@ # SaxOnlyValidator : guarantees XML is well formed # None : do not try to validate anything # validators used must be imported from from.devtools.htmlparser - validators = { - # maps vid : validator name - 'hcal' : SaxOnlyValidator, - 'rss' : SaxOnlyValidator, - 'rssitem' : None, - 'xml' : SaxOnlyValidator, - 'xmlitem' : None, - 'xbel' : SaxOnlyValidator, - 'xbelitem' : None, - 'vcard' : None, - 'fulltext': None, - 'fullthreadtext': None, - 'fullthreadtext_descending': None, - 'text' : None, - 'treeitemview': None, - 'textincontext' : None, - 'textoutofcontext' : None, - 'combobox' : None, - 'csvexport' : None, - 'ecsvexport' : None, + content_type_validators = { + # maps MIME type : validator name + # + # do not set html validators here, we need HTMLValidator for html + # snippets + #'text/html': DTDValidator, + #'application/xhtml+xml': DTDValidator, + 'application/xml': SaxOnlyValidator, + 'text/xml': SaxOnlyValidator, + 'text/plain': None, + 'text/comma-separated-values': None, + 'text/x-vcard': None, + 'text/calendar': None, + 'application/json': None, + 'image/png': None, } - valmap = {None: None, 'dtd': DTDValidator, 'xml': SaxOnlyValidator} - no_auto_populate = () - ignored_relations = () + # maps vid : validator name (override content_type_validators) + vid_validators = dict((vid, VALMAP[valkey]) + for vid, valkey in VIEW_VALIDATORS.iteritems()) - def __init__(self, *args, **kwargs): - EnvBasedTC.__init__(self, *args, **kwargs) - for view, valkey in VIEW_VALIDATORS.iteritems(): - self.validators[view] = self.valmap[valkey] + no_auto_populate = () + ignored_relations = () def custom_populate(self, how_many, cursor): pass @@ -163,21 +158,24 @@ self.commit() @nocoverage - def _check_html(self, output, vid, template='main'): + def _check_html(self, output, view, template='main'): """raises an exception if the HTML is invalid""" - if template is None: - default_validator = HTMLValidator - else: - default_validator = DTDValidator - validatorclass = self.validators.get(vid, default_validator) + try: + validatorclass = self.vid_validators[view.id] + except KeyError: + if template is None: + default_validator = HTMLValidator + else: + default_validator = DTDValidator + validatorclass = self.content_type_validators.get(view.content_type, + default_validator) if validatorclass is None: return None validator = validatorclass() - output = output.strip() - return validator.parse_string(output) + return validator.parse_string(output.strip()) - def view(self, vid, rset, req=None, template='main', htmlcheck=True, **kwargs): + def view(self, vid, rset, req=None, template='main', **kwargs): """This method tests the view `vid` on `rset` using `template` If no error occured while rendering the view, the HTML is analyzed @@ -194,8 +192,6 @@ # print req.form['vid'] = vid view = self.vreg.select_view(vid, req, rset, **kwargs) - if view.content_type not in ('application/xml', 'application/xhtml+xml', 'text/html'): - htmlcheck = False # set explicit test description if rset is not None: self.set_description("testing %s, mod=%s (%s)" % (vid, view.__module__, rset.printable_rql())) @@ -207,15 +203,18 @@ elif template == 'main': _select_view_and_rset = TheMainTemplate._select_view_and_rset # patch TheMainTemplate.process_rql to avoid recomputing resultset - TheMainTemplate._select_view_and_rset = lambda *a, **k: (view, rset) + def __select_view_and_rset(self, view=view, rset=rset): + self.rset = rset + return view, rset + TheMainTemplate._select_view_and_rset = __select_view_and_rset try: - return self._test_view(viewfunc, vid, htmlcheck, template, **kwargs) + return self._test_view(viewfunc, view, template, **kwargs) finally: if template == 'main': TheMainTemplate._select_view_and_rset = _select_view_and_rset - def _test_view(self, viewfunc, vid, htmlcheck=True, template='main', **kwargs): + def _test_view(self, viewfunc, view, template='main', **kwargs): """this method does the actual call to the view If no error occured while rendering the view, the HTML is analyzed @@ -227,10 +226,7 @@ output = None try: output = viewfunc(**kwargs) - if htmlcheck: - return self._check_html(output, vid, template) - else: - return output + return self._check_html(output, view, template) except (SystemExit, KeyboardInterrupt): raise except: @@ -238,19 +234,16 @@ # is not an AssertionError klass, exc, tcbk = sys.exc_info() try: - msg = '[%s in %s] %s' % (klass, vid, exc) + msg = '[%s in %s] %s' % (klass, view.id, exc) except: - msg = '[%s in %s] undisplayable exception' % (klass, vid) + msg = '[%s in %s] undisplayable exception' % (klass, view.id) if output is not None: position = getattr(exc, "position", (0,))[0] if position: # define filter - - output = output.splitlines() width = int(log(len(output), 10)) + 1 line_template = " %" + ("%i" % width) + "i: %s" - # XXX no need to iterate the whole file except to get # the line number output = '\n'.join(line_template % (idx + 1, line) @@ -259,10 +252,13 @@ msg+= '\nfor output:\n%s' % output raise AssertionError, msg, tcbk - + + def to_test_etypes(self): + return unprotected_entities(self.schema, strict=True) + def iter_automatic_rsets(self): """generates basic resultsets for each entity type""" - etypes = unprotected_entities(self.schema, strict=True) + etypes = self.to_test_etypes() for etype in etypes: yield self.execute('Any X WHERE X is %s' % etype) @@ -281,23 +277,26 @@ """returns the list of views that can be applied on `rset`""" req = rset.req only_once_vids = ('primary', 'secondary', 'text') - skipped = ('restriction', 'cell') req.data['ex'] = ValueError("whatever") for vid, views in self.vreg.registry('views').items(): if vid[0] == '_': continue - try: - view = self.vreg.select(views, req, rset) - if view.id in skipped: - continue - if view.category == 'startupview': + if rset.rowcount > 1 and vid in only_once_vids: + continue + views = [view for view in views + if view.category != 'startupview' + and not issubclass(view, NotificationView)] + if views: + try: + view = self.vreg.select(views, req, rset) + if view.linkable(): + yield view + else: + not_selected(self.vreg, view) + # else the view is expected to be used as subview and should + # not be tested directly + except NoSelectableObject: continue - if rset.rowcount > 1 and view.id in only_once_vids: - continue - if not isinstance(view, NotificationView): - yield view - except NoSelectableObject: - continue def list_actions_for(self, rset): """returns the list of actions that can be applied on `rset`""" @@ -305,27 +304,25 @@ for action in self.vreg.possible_objects('actions', req, rset): yield action - def list_boxes_for(self, rset): """returns the list of boxes that can be applied on `rset`""" req = rset.req for box in self.vreg.possible_objects('boxes', req, rset): yield box - def list_startup_views(self): """returns the list of startup views""" req = self.request() for view in self.vreg.possible_views(req, None): - if view.category != 'startupview': - continue - yield view.id - + if view.category == 'startupview': + yield view.id + else: + not_selected(self.vreg, view) + def _test_everything_for(self, rset): """this method tries to find everything that can be tested for `rset` and yields a callable test (as needed in generative tests) """ - rqlst = parse(rset.rql) propdefs = self.vreg['propertydefs'] # make all components visible for k, v in propdefs.items(): @@ -335,7 +332,7 @@ backup_rset = rset._prepare_copy(rset.rows, rset.description) yield InnerTest(self._testname(rset, view.id, 'view'), self.view, view.id, rset, - rset.req.reset_headers(), 'main', not view.binary) + rset.req.reset_headers(), 'main') # We have to do this because some views modify the # resultset's syntax tree rset = backup_rset @@ -348,8 +345,6 @@ for box in self.list_boxes_for(rset): yield InnerTest(self._testname(rset, box.id, 'box'), box.dispatch) - - @staticmethod def _testname(rset, objid, objtype): return '%s_%s_%s' % ('_'.join(rset.column_types(0)), objid, objtype) @@ -390,4 +385,36 @@ rset2 = rset.limit(limit=1, offset=row) yield rset2 +def not_selected(vreg, vobject): + try: + vreg._selected[vobject.__class__] -= 1 + except (KeyError, AttributeError): + pass +def vreg_instrumentize(testclass): + from cubicweb.devtools.apptest import TestEnvironment + env = testclass._env = TestEnvironment('data', configcls=testclass.configcls, + requestcls=testclass.requestcls) + vreg = env.vreg + vreg._selected = {} + orig_select = vreg.__class__.select + def instr_select(self, *args, **kwargs): + selected = orig_select(self, *args, **kwargs) + try: + self._selected[selected.__class__] += 1 + except KeyError: + self._selected[selected.__class__] = 1 + except AttributeError: + pass # occurs on vreg used to restore database + return selected + vreg.__class__.select = instr_select + +def print_untested_objects(testclass, skipregs=('hooks', 'etypes')): + vreg = testclass._env.vreg + for registry, vobjectsdict in vreg.items(): + if registry in skipregs: + continue + for vobjects in vobjectsdict.values(): + for vobject in vobjects: + if not vreg._selected.get(vobject): + print 'not tested', registry, vobject diff -r a81d3babb582 -r 85eae6a79f75 doc/book/en/A03a-concepts.en.txt --- a/doc/book/en/A03a-concepts.en.txt Thu Jan 15 10:13:25 2009 +0100 +++ b/doc/book/en/A03a-concepts.en.txt Wed Jun 03 19:42:23 2009 +0200 @@ -11,7 +11,7 @@ .. image:: images/archi_globale.en.png -`CubicWeb` framework is a server/client application framework. Those two +`CubicWeb` framework is a server/client application framework. Those two parties communicates through RQL (`CubicWeb` query language implementation) and ResultSet (which will be explained in :ref:`TermsVocabulary`). @@ -44,7 +44,7 @@ stored in the database at the time an instance is created. `CubicWeb` provides a certain number of system entities included automatically as it is necessary for the core of `CubicWeb` and a library of - cubes (which defined application entities) that can be explicitely + cubes (which defined application entities) that can be explicitely included if necessary. *entity type* @@ -69,7 +69,7 @@ *relation definition* A relation definition is a 3-uple (subject entity type, relation type, object entity type), with an associated set of property such as cardinality, constraints... - + *repository* This is the RQL server side of `CubicWeb`. Be carefull not to get confused with a Mercurial repository or a debian repository. @@ -77,7 +77,7 @@ *source* A data source is a container of data (SGBD, LDAP directory, `Google App Engine`'s datastore ...) integrated in the - `CubicWeb` repository. This repository has at least one source, `system` which + `CubicWeb` repository. This repository has at least one source, `system` which contains the schema of the application, plain-text index and others vital informations for the system. @@ -86,7 +86,7 @@ - ``repository`` : repository only, accessible for clients using Pyro - ``twisted`` : web interface only, access the repository using Pyro - - ``all-in-one`` : web interface and repository in a single process. + - ``all-in-one`` : web interface and repository in a single process. The repository could be or not accessible using Pyro. *cube* @@ -94,7 +94,7 @@ to provide a specific functionnality or a complete `CubicWeb` application potentially using other cubes. The available cubes are located in the file system at `/path/to/forest/cubicweb/cubes` for a Mercurial forest installation, - for a debian packages installation they will be located in + for a debian packages installation they will be located in `/usr/share/cubicweb/cubes`. Larger applications can be built faster by importing cubes, adding entities and relationships and overriding the @@ -102,7 +102,7 @@ cubes. *instance* - An instance is a specific installation of one or multiple cubes. All the required + An instance is a specific installation of one or multiple cubes. All the required configuration files necessary for the well being of your web application are grouped in an instance. This will refer to the cube(s) your application is based on. @@ -112,7 +112,7 @@ *application* The term application is sometime used to talk about an instance - and sometimes to talk of a cube depending on the context. + and sometimes to talk of a cube depending on the context. So we would like to avoid using this term and try to use *cube* and *instance* instead. @@ -127,7 +127,7 @@ *query language* A full-blown query language named RQL is used to formulate requests - to the database or any sources such as LDAP or `Google App Engine`'s + to the database or any sources such as LDAP or `Google App Engine`'s datastore. *views* @@ -147,7 +147,7 @@ This query language is inspired by SQL but is highest level, its implementation generates SQL. - + .. _`Python Remote Object`: http://pyro.sourceforge.net/ .. _`yams`: http://www.logilab.org/project/yams/ @@ -194,14 +194,14 @@ ~~~~~~~~~~~~~~ The Python API developped to interface with RQL is inspired from the standard db-api, -with a Connection object having the methods cursor, rollback and commit essentially. +with a Connection object having the methods cursor, rollback and commit essentially. The most important method is the `execute` method of a cursor : `execute(rqlstring, args=None, eid_key=None, build_descr=True)` :rqlstring: the RQL query to execute (unicode) :args: if the query contains substitutions, a dictionnary containing the values to use -:eid_key: +:eid_key: an implementation detail of the RQL queries cache implies that if a substitution is used to introduce an eid *susceptible to raise the ambiguities in the query type resolution*, then we have to specify the correponding key in the dictionnary @@ -210,14 +210,14 @@ 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:: While executing updates queries (SET, INSERT, DELETE), if a query generates an error related to security, a rollback is automatically done on the current transaction. - + The `Request` class (`cubicweb.web`) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -243,7 +243,7 @@ key or the value `default` if the key is not defined * `set_session_data(key, value)`, assign a value to a key * `del_session_data(key)`, suppress the value associated to a key - + :Cookies handling: * `get_cookie()`, returns a dictionnary containing the value of the header @@ -290,7 +290,7 @@ `__registry__`) and its identifier (attribute `id`). Usually we do not have to take care of the register, only the identifier `id`. -We can find a certain number of attributes and methods defined in this class +We can find a certain number of attributes and methods defined in this class and common to all the application objects. At the recording, the following attributes are dynamically added to @@ -333,7 +333,7 @@ also call the method `complete()` on the entity before returning it :Data formatting: - * `format_date(date, date_format=None, time=False)` returns a string for a + * `format_date(date, date_format=None, time=False)` returns a string for a mx date time according to application's configuration * `format_time(time)` returns a string for a mx date time according to application's configuration @@ -342,8 +342,8 @@ * `external_resource(rid, default=_MARKER)`, access to a value defined in the configuration file `external_resource` - - * `tal_render(template, variables)`, renders a precompiled page template with + + * `tal_render(template, variables)`, renders a precompiled page template with variables in the given dictionary as context .. note:: @@ -357,7 +357,7 @@ PrimaryView.f(self, arg1) You'd better write: :: - + class Truc(PrimaryView): def f(self, arg1): super(Truc, self).f(arg1) @@ -374,7 +374,7 @@ in order to provide a specific feature or even a complete application using others cubes. -You can decide to write your own set of cubes if you wish to re-use the +You can decide to write your own set of cubes if you wish to re-use the entity types you develop. Lots of cubes are available from the `CubicWeb Forge`_ under a free software license. @@ -388,12 +388,12 @@ A cube is structured as follows: :: - + mycube/ | |-- data/ | |-- cubes.mycube.css - | |-- cubes.mycube.js + | |-- cubes.mycube.js | `-- external_resources | |-- debian/ @@ -437,12 +437,12 @@ | `-- views.py - + We can use subpackages instead of python modules for ``views.py``, ``entities.py``, ``schema.py`` or ``hooks.py``. For example, we could have: :: - + mycube/ | |-- entities.py @@ -451,7 +451,7 @@ |-- forms.py |-- primary.py `-- widgets.py - + where : @@ -472,13 +472,13 @@ * file ``__pkginfo__.py`` provides component meta-data, especially the distribution and the current version (server side and web interface) or sub-cubes used by the cube. - - + + At least you should have: * the file ``__pkginfo__.py`` * the schema definition - XXX false, we may want to have cubes which are only adding a service, + XXX false, we may want to have cubes which are only adding a service, no persistent data (eg embeding for instance) diff -r a81d3babb582 -r 85eae6a79f75 doc/book/en/B0011-schema-stdlib.en.txt --- a/doc/book/en/B0011-schema-stdlib.en.txt Thu Jan 15 10:13:25 2009 +0100 +++ b/doc/book/en/B0011-schema-stdlib.en.txt Wed Jun 03 19:42:23 2009 +0200 @@ -37,34 +37,54 @@ An application is based on several basic cubes. In the set of available basic cubes we can find for example : -* `comment`, provides an entity type for `Comment` allowing us to comment others - site's entities - -* `mailinglist`, provides an entity type for `Mailinglist` which groups informations - in a discussion list +* addressbook_: PhoneNumber and PostalAddress -* `file`, provides entity types for `File` et `Image` used to represent - files (text or binary) with additionnal informations such as MIME type or - encoding. - -* `link`, provides an entity type for hypertext link (`Link`) +* basket_: Basket (like a shopping cart) -* `blog`, provides an entity type weblog (`Blog`) - -* `person`, provides an entity type for a person (`Person`) +* blog_: Blog (a *very* basic blog) -* `addressbook`, provides an entity type used to represent phone - numbers (`PhoneNumber`) and mailing address (`PostalAddress`) - -* `classtags`, categorization system based on tags (`Tag`) +* comment_: Comment (to attach comment threads to entities) -* `classfolders`, categorization system based on folders hierarchy in order - to create navigation sections (`Folder`) - -* `email`, archiving management for emails (`Email`, `Emailpart`, +* email_: archiving management for emails (`Email`, `Emailpart`, `Emailthread`) -* `basket`, basket management (`Basket`) allowing to group entities +* event_: Event (define events, display them in calendars) + +* file_: File (to allow users to upload and store binary or text files) + +* folder_: Folder (to organize things but grouping them in folders) + +* keyword_: Keyword (to define classification schemes) + +* link_: Link (to collect links to web resources) + +* mailinglist_: MailingList (to reference a mailing-list and the URLs + for its archives and its admin interface) + +* person_: Person (easily mixed with addressbook) + +* tag_: Tag (to tag anything) + +* task_: Task (something to be done between start and stop date) + +* zone_: Zone (to define places within larger places, for example a + city in a state in a country) + +.. _addressbook: http://www.cubicweb.org/project/cubicweb-addressbook +.. _basket: http://www.cubicweb.org/project/cubicweb-basket +.. _blog: http://www.cubicweb.org/project/cubicweb-blog +.. _comment: http://www.cubicweb.org/project/cubicweb-comment +.. _email: http://www.cubicweb.org/project/cubicweb-email +.. _event: http://www.cubicweb.org/project/cubicweb-event +.. _file: http://www.cubicweb.org/project/cubicweb-file +.. _folder: http://www.cubicweb.org/project/cubicweb-folder +.. _keyword: http://www.cubicweb.org/project/cubicweb-keyword +.. _link: http://www.cubicweb.org/project/cubicweb-link +.. _mailinglist: http://www.cubicweb.org/project/cubicweb-mailinglist +.. _person: http://www.cubicweb.org/project/cubicweb-person +.. _tag: http://www.cubicweb.org/project/cubicweb-tag +.. _task: http://www.cubicweb.org/project/cubicweb-task +.. _zone: http://www.cubicweb.org/project/cubicweb-zone To declare the use of a component, once installed, add the name of the component to the variable `__use__` in the file `__pkginfo__.py` of your own component. diff -r a81d3babb582 -r 85eae6a79f75 doc/book/en/B0020-define-workflows.en.txt --- a/doc/book/en/B0020-define-workflows.en.txt Thu Jan 15 10:13:25 2009 +0100 +++ b/doc/book/en/B0020-define-workflows.en.txt Wed Jun 03 19:42:23 2009 +0200 @@ -155,4 +155,3 @@ defined by the workflow. This transition, as defined in the workflow, will only being displayed for the users belonging to the group moderators of managers. - diff -r a81d3babb582 -r 85eae6a79f75 doc/book/en/B0031-define-entities.en.txt --- a/doc/book/en/B0031-define-entities.en.txt Thu Jan 15 10:13:25 2009 +0100 +++ b/doc/book/en/B0031-define-entities.en.txt Wed Jun 03 19:42:23 2009 +0200 @@ -55,13 +55,21 @@ It is possible to manage attributes/relations in the simple or multiple editing form thanks to the following *rtags*: -* `primary`, indicates that an attribute or a relation has to be inserted - in the simple or multiple editing forms. In the case of a relation, - the related entity editing form will be included in the editing form. +* `primary`, indicates that an attribute or a relation has to be + inserted **in the simple or multiple editing forms**. In the case of + a relation, the related entity editing form will be included in the + editing form and represented as a combobox. Each item of the + combobox is a link to an existing entity. -* `secondary`, indicates that an attribute or a relation has to be inserted - in the simple editing form only. In the case of a relation, the related - entity editing form sill be included in the editing form. +* `secondary`, indicates that an attribute or a relation has to be + inserted **in the simple editing form only**. In the case of a + relation, the related entity editing form will be included in the + editing form and represented as a combobox. Each item of the combobox + is a link to an existing entity. + +* `inlineview`, includes the target entity's form in the editing form + of the current entity. It allows to create the target entity in the + same time as the current entity. * `generic`, indicates that a relation has to be inserted in the simple editing form, in the generic box of relation creation. diff -r a81d3babb582 -r 85eae6a79f75 doc/book/en/B1030-form-management.en.txt --- a/doc/book/en/B1030-form-management.en.txt Thu Jan 15 10:13:25 2009 +0100 +++ b/doc/book/en/B1030-form-management.en.txt Wed Jun 03 19:42:23 2009 +0200 @@ -13,7 +13,7 @@ The form generated by default does not fit your needs? You are not required to re-do all by hands! :) -* rtags primary, secondary, generated, generic, +* rtags primary, secondary, generated, generic, `Entity.relation_category(rtype, x='subject')` * inline_view (now a rtag?) * widget specification @@ -36,8 +36,8 @@ starting by `eid:` and also having a parameter `__type` associated (also *qualified* by eid) -2. For all the attributes and the relations of an entity to edit: - +2. For all the attributes and the relations of an entity to edit: + 1. search for a parameter `edits-` or `edito-` qualified in the case of a relation where the entity is object 2. if found, the value returned is considered as the initial value @@ -50,24 +50,24 @@ 1. if a qualified parameter `__linkto` is specified, its value has to be a string (or a list of string) such as: :: - + :: - + where is either `subject` or `object` and each eid could be separated from the others by a `_`. Target specifies if the *edited entity* is subject or object of the relation and each relation specified will be inserted. 2. if a qualified parameter `__clone_eid` is specified for an entity, the - relations of the specified entity passed as value of this parameter are + relations of the specified entity passed as value of this parameter are copied on the edited entity. 3. if a qualified parameter `__delete` is specified, its value must be a string or a list of string such as follows: :: - + :: - where each eid subject or object can be seperated from the other + where each eid subject or object can be seperated from the other by `_`. Each relation specified will be deleted. 4. if a qualified parameter `__insert` is specified, its value should @@ -84,14 +84,14 @@ .. note:: - + * If the parameter `__action_delete` is found, all the entities specified as to be edited will be deleted. - + * If the parameter`__action_cancel` is found, no action is completed. - * If the parameter `__action_apply` is found, the editing is applied - normally but the redirection is done on the form + * If the parameter `__action_apply` is found, the editing is applied + normally but the redirection is done on the form (see :ref:`RedirectionControl`). * The parameter `__method` is also supported as for the main template @@ -120,7 +120,7 @@ * `__redirectparams`: forms parameters to add to the path -* `__redirectrql`: redirection RQL request +* `__redirectrql`: redirection RQL request * `__redirectvid`: redirection view identifier @@ -132,6 +132,6 @@ * `__form_id`: initial view form identifier, used if `__action_apply` is found -In general we use either `__redirectpath` and `__redirectparams` or +In general we use either `__redirectpath` and `__redirectparams` or `__redirectrql` and `__redirectvid`. diff -r a81d3babb582 -r 85eae6a79f75 doc/book/en/B1060-templates.en.txt --- a/doc/book/en/B1060-templates.en.txt Thu Jan 15 10:13:25 2009 +0100 +++ b/doc/book/en/B1060-templates.en.txt Wed Jun 03 19:42:23 2009 +0200 @@ -196,11 +196,9 @@ the case we want to create new CSS style, the best is to define it a in a new CSS located under ``myapp/data/``. -If you want to modify an existing CSS styling property, you will have to use -``!important`` declaration to override the existing property. The application -apply a higher priority on the default CSS and you can not change that. -Customized CSS will not be read first. +.. [TRANSLATE ME FROM FRENCH] +.. 03-XX-external_resources.fr.txt [TODO] Add login menu in left column diff -r a81d3babb582 -r 85eae6a79f75 doc/book/en/C011-installation.en.txt --- a/doc/book/en/C011-installation.en.txt Thu Jan 15 10:13:25 2009 +0100 +++ b/doc/book/en/C011-installation.en.txt Wed Jun 03 19:42:23 2009 +0200 @@ -67,9 +67,9 @@ Then you can install: -* `pyro` if you wish the repository is accessible through Pyro +* `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 - (in suche case the packages will have to be isntalled on both + (in which case the packages will have to be installed on both machines) * `python-ldap` if you plan to use a LDAP source on the server @@ -80,8 +80,8 @@ ------------------------- If you installed `CubicWeb` by cloning the Mercurial forest, then you -will need to update the environment variable PYTHONPATH to add to it -the path to the forest ``cubicweb``. +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 :: @@ -117,16 +117,13 @@ createuser -s username - Initialize the password of the superuser you juste created with + Initialize the password of the superuser you just created with ``su - postgres`` and ``psql``. This password will be asked to you later on where you will create an instance with `cubicweb-ctl create` -[XXX] -Est-ce que ces etapes sont vraiment necessaires? -sand : lors de l'installation de ma bdd cela n'a pas ete fait -et il semble que tout aille bien. Doit etre verifie avec les experts. +.. [XXX] Est-ce que ces etapes sont vraiment necessaires? sand : lors de l'installation de ma bdd cela n'a pas ete fait et il semble que tout aille bien. Doit etre verifie avec les experts. * installation of plain-text index extension :: @@ -140,7 +137,7 @@ Pyro configuration ------------------ -If you use Pyro, it is required to have a name server Pyro runing on your +If you use Pyro, it is required to have a name server Pyro running on your network (by default it is identified by a broadcast request). To do so, you need to : @@ -151,6 +148,6 @@ `pyro-nsd start` * edit the file ``/etc/default/pyro-nsd`` so that the name server pyro - will be launched automatically when the macine fire up + will be launched automatically when the machine fire up diff -r a81d3babb582 -r 85eae6a79f75 doc/book/en/C012-create-instance.en.txt --- a/doc/book/en/C012-create-instance.en.txt Thu Jan 15 10:13:25 2009 +0100 +++ b/doc/book/en/C012-create-instance.en.txt Wed Jun 03 19:42:23 2009 +0200 @@ -24,11 +24,11 @@ A cube defines entities, their views, their schemas and workflows in an independant directory located in ``/path/to/forest/cubicweb/cubes/`` -for a Mercurila installation or in ``/usr/share/cubicweb/cubes`` for -a debian packages installation. +for a Mercurial installation or in ``/usr/share/cubicweb/cubes`` for +a debian package installation. When an instance is created, you list one or more cubes that your instance -will use. Use a cube means having the entities defined in your cube's schema +will 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. .. note:: diff -r a81d3babb582 -r 85eae6a79f75 doc/book/en/Z010-beginners.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/Z010-beginners.en.txt Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,12 @@ +.. -*- coding: utf-8 -*- + +.. _QuickInstall: + +=========================================== +Quick Installation of a `CubicWeb` instance +=========================================== + +.. include:: C011-installation.en.txt +.. include:: Z012-create-instance.en.txt + + diff -r a81d3babb582 -r 85eae6a79f75 doc/book/en/Z012-create-instance.en.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/Z012-create-instance.en.txt Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,79 @@ +.. -*- coding: utf-8 -*- + +Creation of your first instance +=============================== + +What is an instance? +-------------------- + +A `CubicWeb` instance is a container that +refers to cubes and configuration parameters for your web application. +Each instance is stored as a directory in ``~/etc/cubicweb.d`` which enables +us to run your application. + +What is a cube? +--------------- + +Cubes represent data and basic building bricks of your web applications : +blogs, person, date, addressbook and a lot more. + +.. XXX They related to each other by a 'Schema' which is also the PostGres representation. + +Each cube defines entities, their views, their schemas and workflows +in an independant directory located in ``/path/to/forest/cubicweb/cubes/`` +for a Mercurial installation or in ``/usr/share/cubicweb/cubes`` for +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. +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 +------------------------------------ + +We can create an instance to view our +application in a web browser. :: + + cubicweb-ctl create blog myblog + +.. XXX or :: + +.. XXX cubicweb-ctl create forge myforge + + +.. note:: + The commands used below are more detailled in the section dedicated to + :ref:`cubicweb-ctl`. + +A series of questions will be prompted to you, the default answer is usually +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`). + +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 +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. +The configuration files are stored in *~/etc/cubicweb.d/myblog/*. + +To launch the web application, you just type :: + + cubicweb-ctl start myblog + +You can see how it looks by +visiting the URL `http://localhost:8080`. +To login, please use the cubicweb administrator login/psswd you +defined when you created the instance. + +To shutdown the instance :: + + cubicweb-ctl stop myinstance + +.. XXX something like `cubicweb-ctl live-server intra` would be nice + + diff -r a81d3babb582 -r 85eae6a79f75 doc/book/en/index.txt --- a/doc/book/en/index.txt Thu Jan 15 10:13:25 2009 +0100 +++ b/doc/book/en/index.txt Wed Jun 03 19:42:23 2009 +0200 @@ -29,7 +29,9 @@ The hacker will join development at the forge_. -The impatient will move right away to :ref:`MiseEnPlaceEnv`. +The impatient will go strait away to :ref:`QuickInstall`. + +The impatient developper will move right away to :ref:`MiseEnPlaceEnv`. .. _Logilab: http://www.logilab.fr/ .. _forge: http://www.cubicweb.org/project/ diff -r a81d3babb582 -r 85eae6a79f75 doc/book/en/makefile --- a/doc/book/en/makefile Thu Jan 15 10:13:25 2009 +0100 +++ b/doc/book/en/makefile Wed Jun 03 19:42:23 2009 +0200 @@ -43,7 +43,7 @@ #apydoc: # epydoc --html -o epydoc/ -n ../server/*.py ../core/*.py ../common/*.py ../server/*/*.py ../modpython/*/*.py ../common/*/*.py apidoc: - epydoc --html -o apidoc -n "cubicweb" --exclude=setup --exclude=__pkginfo__ ../ + epydoc --html -o apidoc -n "cubicweb" --exclude=setup --exclude=__pkginfo__ ../../../ # run sphinx ### html: diff -r a81d3babb582 -r 85eae6a79f75 doc/book/fr/03-XX-external_resources.fr.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/fr/03-XX-external_resources.fr.txt Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,135 @@ +.. -*- coding: utf-8 -*- + + +Les ressources externes +======================= + +Les ressources externes à une application regroupent l'ensemble des fichiers qui seront chargés dans l'entête des pages XHTML générées. +Elles sont donc constituées principalement des feuilles de styles, des scripts javascripts et de certaines ressources graphiques comme l'icône favicon par exemple. + + +Liste des feuilles de styles utilisées par défaut +------------------------------------------------- + +Les fichiers par défaut se trouve dans les sources du framework. En voici le tableau récapitulatif: + ++--------------------------------------+----------------------------------------------------+-----------------------------------+ +| Fichiers | Utilisation | Vues ou widget concernés | ++======================================+====================================================+===================================+ +| web/data/cubicweb.acl.css | formulaires pour le contrôle aux accès | editgroups, security | +| web/data/cubicweb.calendar.css | calendriers | onemonthcal, oneweekcal | +| web/data/cubicweb.calendar_popup.css | popup calendriers | DateWidget | +| web/data/cubicweb.css | gabarit principal de l'application | | +| web/data/cubicweb.facets.css | surcharge du `MIT Simile Exhibit Web Widgets`_ | filter_box | +| web/data/cubicweb.form.css | formulaires | creation, inline-creation, copya, | +| | | inline-edition, edition, muledit | +| web/data/cubicweb.html_tree.css | style pour les widgets d'arborescence | | +| web/data/cubicweb.ie.css | dédié aux comportements de Internet Explorer | | +| web/data/cubicweb.iprogress.css | style pour les widgets d'avancement | | +| web/data/cubicweb.login.css | page et popup d'authentification | logform | +| web/data/cubicweb.mailform.css | style utilisé dans les formulaires d'envoi de mail | | +| web/data/cubicweb.preferences.css | style pour la page des préférences utilisateurs | systemepropertiesform | +| web/data/cubicweb.print.css | style dédié à l'impression | | +| web/data/cubicweb.schema.css | style dédié au schéma de l'application | | +| web/data/cubicweb.suggest.css | surcharge utilisée pour les suggestions | | +| web/data/cubicweb.tablesorter.css | surcharge pour le tri dans les tableau | | +| web/data/cubicweb.tableview.css | surcharge pour le tri sélectif | | +| web/data/cubicweb.timetable.css | style pour le widget Timetable | timetable | +| web/data/jquery.autocomplete.css | surcharge pour le widget `jQuery autocompleter`_ | | +| web/data/jquery.treeview.css | surcharge pour le widget `jQuery treeview`_ | | +| web/data/pygments.css | style pour la coloration des blocs de code | | +| web/data/timeline-bundle.css | surcharge du `MIT Simile Timeline Web Widgets`_ | TimelineWidget | +| web/data/ui.tabs.css | surcharge pour le widget Tabs de `jQuery UI`_ | | ++--------------------------------------+----------------------------------------------------+-----------------------------------+ + +.. _MIT Simile Exhibit Web Widgets: http://code.google.com/p/simile-widgets/wiki/Exhibit +.. _MIT Simile Timeline Web Widgets: http://code.google.com/p/simile-widgets/wiki/Timeline +.. _jQuery autocompleter: http://www.dyve.net/jquery/?autocomplete +.. _jQuery treeview: http://plugins.jquery.com/project/treeview +.. _jQuery UI: http://docs.jquery.com/UI + +D'une manière générale, si vous réutiliser un nom de fichier existant, vous écrasez le contenu du fichier d'origine. + + +Changer les feuilles de styles +------------------------------ + +Configuration statique +~~~~~~~~~~~~~~~~~~~~~~ +Dans les sources de votre nouveau cube, vous devez éditer le fichier *data/external_resources* et définir la variable de configuration: + + # CSS stylesheets to include in HTML headers + # uncomment the line below to use template specific stylesheet + STYLESHEETS = DATADIR/cubicweb.css + +Les styles sont définis dans le fichier external_resources par 3 variables: + +- la variable STYLESHEETS est défine pour tous les types de médias +- la variable STYLESHEETS_PRINT sont les styles applicables pour l'impression +- la variable IE_STYLESHEETS s'appliquent uniquement aux versions d'Internet Explorer + +En copiant le fichier d'origine **cubicweb.css**, il est alors possible de modifier le gabarit de base du framework CubicWeb. +Il est également possible de réutiliser le fichier d'origine. + +En créant un nouveau fichier **cubes.(le_nom_du_cube).css** dans le répertoire **data/** et en ajoutant une directive css @import, il est possible de réutiliser les styles définis par défaut: + + @import url("cubicweb.css"); + + +Chargement dynamique de feuilles de style dans vos vues +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Il est possible de charger des css spécifiques pour une vue par l'utilisation de la méthode add_css(): + + self.req.add_css('mon_cube.css') + + +Les ressources graphiques de base +--------------------------------- + +Vous pouvez changer certaines ressources graphiques comme: + +- le logo du site: + + # path to the logo (relative to the application main script, seen as a + # directory, hence .. when you are not using an absolute path) + LOGO = DATADIR/logo.png + +- la 'favicon' du site: + + FAVICON = DATADIR/favicon.ico + +- le logo des flux RSS: + + RSS_LOGO = DATADIR/rss.png + +- l'icône permettant l'appel au widget 'calendrier': + + CALENDAR_ICON = DATADIR/calendar.png + +- l'icône utilisée pour la validation d'une recherche: + + SEARCH_GO = DATADIR/go.png + +- l'icône d'aide en ligne: + + HELP = DATADIR/help.png + + +Ajouter vos scripts javascripts +------------------------------- + +Configuration statique +~~~~~~~~~~~~~~~~~~~~~~ +Vous devez surcharger la variable JAVASCRIPTS dans le fichier *data/external_resources* de votre cube. + +Chargement dynamique de script javascript dans vos vues +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Il est possible de charger vos scripts par la méthode add_js(): + + self.req.add_js('mon_script.js') + + +Problèmes connus +---------------- + +Il est important de noter que la méthode de chargement dynamique ne marche pas avec les widgets Ajax. Vos fichiers devront déjà être au préalable avoir été chargés. diff -r a81d3babb582 -r 85eae6a79f75 doc/book/fr/makefile --- a/doc/book/fr/makefile Thu Jan 15 10:13:25 2009 +0100 +++ b/doc/book/fr/makefile Wed Jun 03 19:42:23 2009 +0200 @@ -43,7 +43,7 @@ #apydoc: # epydoc --html -o epydoc/ -n ../server/*.py ../core/*.py ../common/*.py ../server/*/*.py ../modpython/*/*.py ../common/*/*.py apidoc: - epydoc --html -o apidoc -n "cubicweb" --exclude=setup --exclude=__pkginfo__ ../ + epydoc --html -o apidoc -n "cubicweb" --exclude=setup --exclude=__pkginfo__ ../../../ # run sphinx ### html: diff -r a81d3babb582 -r 85eae6a79f75 entities/__init__.py --- a/entities/__init__.py Thu Jan 15 10:13:25 2009 +0100 +++ b/entities/__init__.py Wed Jun 03 19:42:23 2009 +0200 @@ -1,7 +1,7 @@ """base application's entities class implementation: `AnyEntity` :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" diff -r a81d3babb582 -r 85eae6a79f75 entities/authobjs.py --- a/entities/authobjs.py Thu Jan 15 10:13:25 2009 +0100 +++ b/entities/authobjs.py Wed Jun 03 19:42:23 2009 +0200 @@ -1,3 +1,10 @@ +"""entity classes user and group entities + +:organization: Logilab +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +""" +__docformat__ = "restructuredtext en" from logilab.common.decorators import cached from cubicweb import Unauthorized @@ -26,6 +33,7 @@ 'in_group' : 'primary', ('owned_by', '*', 'object') : ('generated', 'link'), ('created_by','*','object') : ('generated', 'link'), + ('bookmarked_by', '*', 'object'): ('generated', 'create'), } # used by repository to check if the user can log in or not @@ -85,6 +93,13 @@ """ return self.matching_groups(group) == 1 + def is_anonymous(self): + """ checks if user is an anonymous user""" + #FIXME on the web-side anonymous user is detected according + # to config['anonymous-user'], we don't have this info on + # the server side. + return self.groups == frozenset(('guests', )) + def owns(self, eid): if hasattr(self.req, 'unsafe_execute'): # use unsafe_execute on the repository side, in case diff -r a81d3babb582 -r 85eae6a79f75 entities/lib.py --- a/entities/lib.py Thu Jan 15 10:13:25 2009 +0100 +++ b/entities/lib.py Wed Jun 03 19:42:23 2009 +0200 @@ -1,7 +1,7 @@ """entity classes for optional library entities :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" diff -r a81d3babb582 -r 85eae6a79f75 goa/__init__.py --- a/goa/__init__.py Thu Jan 15 10:13:25 2009 +0100 +++ b/goa/__init__.py Wed Jun 03 19:42:23 2009 +0200 @@ -28,7 +28,7 @@ regular python datetime object """ if yamstype is None: - yamstype = guess_yamstype_from_date(datetimeobj) + yamstype = guess_yamstype_for_date(datetimeobj) assert yamstype is not None if yamstype == 'Datetime': # don't use date, db model doesn't actually support it, only datetime diff -r a81d3babb582 -r 85eae6a79f75 goa/appobjects/components.py --- a/goa/appobjects/components.py Thu Jan 15 10:13:25 2009 +0100 +++ b/goa/appobjects/components.py Wed Jun 03 19:42:23 2009 +0200 @@ -15,7 +15,7 @@ from cubicweb.schema import display_name from cubicweb.common.view import StartupView, EntityView from cubicweb.common.selectors import (one_line_rset, match_search_state, - accept_selector) + accept) from cubicweb.web import Redirect from cubicweb.web.views import vid_from_rset from cubicweb.goa.db import rset_from_objs @@ -31,7 +31,7 @@ """ id = 'search-associate' - __selectors__ = (one_line_rset, match_search_state, accept_selector) + __selectors__ = (one_line_rset, match_search_state, accept) accepts = ('Any',) search_states = ('linksearch',) diff -r a81d3babb582 -r 85eae6a79f75 goa/db.py --- a/goa/db.py Thu Jan 15 10:13:25 2009 +0100 +++ b/goa/db.py Wed Jun 03 19:42:23 2009 +0200 @@ -391,7 +391,7 @@ @classmethod def kind(cls): - return self.id + return cls.id @classmethod def properties(cls): diff -r a81d3babb582 -r 85eae6a79f75 goa/overrides/rqlannotation.py --- a/goa/overrides/rqlannotation.py Thu Jan 15 10:13:25 2009 +0100 +++ b/goa/overrides/rqlannotation.py Wed Jun 03 19:42:23 2009 +0200 @@ -1,4 +1,4 @@ -def set_qdata(union, noinvariant): +def set_qdata(getrschema, union, noinvariant): pass class SQLGenAnnotator(object): diff -r a81d3babb582 -r 85eae6a79f75 goa/test/data/bootstrap_cubes diff -r a81d3babb582 -r 85eae6a79f75 goa/test/data/bootstrap_packages diff -r a81d3babb582 -r 85eae6a79f75 goa/test/unittest_editcontroller.py --- a/goa/test/unittest_editcontroller.py Thu Jan 15 10:13:25 2009 +0100 +++ b/goa/test/unittest_editcontroller.py Wed Jun 03 19:42:23 2009 +0200 @@ -17,7 +17,7 @@ config.global_set_option('use-google-auth', False) config.global_set_option('schema-type', 'yams') config.global_set_option('included-cubes', ()) - config.global_set_option('included-yams-cubes', ('eblog',)) + config.global_set_option('included-yams-cubes', ('blog',)) MODEL_CLASSES = () from cubicweb.web.views import editcontroller diff -r a81d3babb582 -r 85eae6a79f75 goa/testlib.py --- a/goa/testlib.py Thu Jan 15 10:13:25 2009 +0100 +++ b/goa/testlib.py Wed Jun 03 19:42:23 2009 +0200 @@ -7,7 +7,7 @@ # additional monkey patches necessary in regular cubicweb environment from cubicweb.server import rqlannotation from cubicweb.goa.overrides import rqlannotation as goarqlannotation -rqlannotation.sqlgen_annotate = goarqlannotation.sqlgen_annotate +rqlannotation.SQLGenAnnotator = goarqlannotation.SQLGenAnnotator rqlannotation.set_qdata = goarqlannotation.set_qdata try: @@ -58,7 +58,7 @@ _DS_TEMPL_FILE = 'tmpdb-template' def load_schema_hook(self, loader): - loader.import_yams_template_schema('data') + loader.import_yams_cube_schema('data') @property def DS_FILE(self): diff -r a81d3babb582 -r 85eae6a79f75 goa/tools/i18n.py --- a/goa/tools/i18n.py Thu Jan 15 10:13:25 2009 +0100 +++ b/goa/tools/i18n.py Wed Jun 03 19:42:23 2009 +0200 @@ -221,7 +221,7 @@ os.chdir(appdirectory) potfiles = [] if osp.exists(osp.join('i18n', 'entities.pot')): - potfiles = potfiles.append( osp.join('i18n', scfile) ) + potfiles = potfiles.append( osp.join('i18n', 'entities.pot') ) print '******** extract schema messages' schemapot = osp.join(tempdir, 'schema.pot') potfiles.append(schemapot) diff -r a81d3babb582 -r 85eae6a79f75 i18n/en.po --- a/i18n/en.po Thu Jan 15 10:13:25 2009 +0100 +++ b/i18n/en.po Wed Jun 03 19:42:23 2009 +0200 @@ -376,6 +376,9 @@ msgid "Problem occured" msgstr "" +msgid "Project linked data" +msgstr "" + msgid "RQLExpression" msgstr "RQL expression" @@ -717,6 +720,9 @@ msgid "add" msgstr "" +msgid "add Bookmark bookmarked_by EUser object" +msgstr "bookmark" + msgid "add EEType add_permission RQLExpression subject" msgstr "rql expression for the add permission" @@ -1311,6 +1317,9 @@ msgid "created_by_object" msgstr "has created" +msgid "creating Bookmark (Bookmark bookmarked_by EUser %(linkto)s)" +msgstr "creating bookmark for %(linkto)s" + msgid "creating EConstraint (EFRDef %(linkto)s constrained_by EConstraint)" msgstr "creating constraint for attribute %(linkto)s" @@ -1668,12 +1677,18 @@ msgid "february" msgstr "" +msgid "file tree view" +msgstr "" + msgid "final" msgstr "" msgid "firstname" msgstr "" +msgid "foaf" +msgstr "" + msgid "follow" msgstr "" @@ -1997,6 +2012,9 @@ msgid "list" msgstr "" +msgid "loading" +msgstr "" + msgid "log in" msgstr "" @@ -2025,6 +2043,9 @@ msgid "manage bookmarks" msgstr "" +msgid "manage permissions" +msgstr "" + msgid "manage security" msgstr "" @@ -2137,6 +2158,9 @@ msgid "not authorized" msgstr "" +msgid "not selected" +msgstr "" + msgid "not specified" msgstr "" @@ -2173,6 +2197,15 @@ msgid "ordernum" msgstr "order" +msgid "owl (tbox+abox)" +msgstr "" + +msgid "owlabox" +msgstr "" + +msgid "owlaboxlight" +msgstr "" + msgid "owned_by" msgstr "owned by" @@ -2416,6 +2449,9 @@ msgid "select this entity" msgstr "" +msgid "selected" +msgstr "" + msgid "semantic description of this attribute" msgstr "" @@ -2541,6 +2577,9 @@ msgid "task progression" msgstr "" +msgid "tbox" +msgstr "" + msgid "text" msgstr "" @@ -2617,6 +2656,9 @@ msgid "transition_of_object" msgstr "use transitions" +msgid "tree view" +msgstr "" + msgid "tuesday" msgstr "" diff -r a81d3babb582 -r 85eae6a79f75 i18n/es.po --- a/i18n/es.po Thu Jan 15 10:13:25 2009 +0100 +++ b/i18n/es.po Wed Jun 03 19:42:23 2009 +0200 @@ -381,6 +381,9 @@ msgid "Problem occured" msgstr "Ha ocurrido un error" +msgid "Project linked data" +msgstr "" + msgid "RQLExpression" msgstr "Expresión RQL" @@ -738,6 +741,9 @@ msgid "add" msgstr "agregar" +msgid "add Bookmark bookmarked_by EUser object" +msgstr "" + msgid "add EEType add_permission RQLExpression subject" msgstr "Definir una expresión RQL de agregación" @@ -1359,6 +1365,9 @@ msgid "created_by_object" msgstr "ha creado" +msgid "creating Bookmark (Bookmark bookmarked_by EUser %(linkto)s)" +msgstr "" + msgid "creating EConstraint (EFRDef %(linkto)s constrained_by EConstraint)" msgstr "creación condicionada por el atributo %(linkto)s" @@ -1745,12 +1754,18 @@ msgid "february" msgstr "febrero" +msgid "file tree view" +msgstr "" + msgid "final" msgstr "final" msgid "firstname" msgstr "nombre" +msgid "foaf" +msgstr "" + msgid "follow" msgstr "seguir la liga" @@ -2090,6 +2105,9 @@ msgid "list" msgstr "liste" +msgid "loading" +msgstr "" + msgid "log in" msgstr "s'identifier" @@ -2118,6 +2136,9 @@ msgid "manage bookmarks" msgstr "gÈrer les signets" +msgid "manage permissions" +msgstr "" + msgid "manage security" msgstr "gestion de la sÈcuritÈ" @@ -2232,6 +2253,9 @@ msgid "not authorized" msgstr "non autorisÈ" +msgid "not selected" +msgstr "" + msgid "not specified" msgstr "non spÈcifiÈ" @@ -2268,6 +2292,15 @@ msgid "ordernum" msgstr "ordre" +msgid "owl (tbox+abox)" +msgstr "" + +msgid "owlabox" +msgstr "" + +msgid "owlaboxlight" +msgstr "" + msgid "owned_by" msgstr "appartient ‡" @@ -2521,6 +2554,9 @@ msgid "select this entity" msgstr "sÈlectionner cette entitÈ" +msgid "selected" +msgstr "" + msgid "semantic description of this attribute" msgstr "description sÈmantique de cet attribut" @@ -2649,6 +2685,9 @@ msgid "task progression" msgstr "avancement de la t‚che" +msgid "tbox" +msgstr "" + msgid "text" msgstr "text" @@ -2726,6 +2765,9 @@ msgid "transition_of_object" msgstr "a pour transition" +msgid "tree view" +msgstr "" + msgid "tuesday" msgstr "mardi" diff -r a81d3babb582 -r 85eae6a79f75 i18n/fr.po --- a/i18n/fr.po Thu Jan 15 10:13:25 2009 +0100 +++ b/i18n/fr.po Wed Jun 03 19:42:23 2009 +0200 @@ -381,6 +381,9 @@ msgid "Problem occured" msgstr "Une erreur est survenue" +msgid "Project linked data" +msgstr "" + msgid "RQLExpression" msgstr "Expression RQL" @@ -740,6 +743,9 @@ msgid "add" msgstr "ajouter" +msgid "add Bookmark bookmarked_by EUser object" +msgstr "signet" + msgid "add EEType add_permission RQLExpression subject" msgstr "définir une expression RQL d'ajout" @@ -1197,7 +1203,7 @@ msgstr "la barre de requête rql, dans l'en-tête de page" msgid "components_rss_feed_url" -msgstr "" +msgstr "syndication rss" msgid "components_rss_feed_url_description" msgstr "" @@ -1361,14 +1367,17 @@ msgid "created_by_object" msgstr "a créé" +msgid "creating Bookmark (Bookmark bookmarked_by EUser %(linkto)s)" +msgstr "création d'un signet pour %(linkto)s" + msgid "creating EConstraint (EFRDef %(linkto)s constrained_by EConstraint)" -msgstr "création contrainte pour l'attribut %(linkto)s" +msgstr "création d'une contrainte pour l'attribut %(linkto)s" msgid "creating EConstraint (ENFRDef %(linkto)s constrained_by EConstraint)" -msgstr "création contrainte pour la relation %(linkto)s" +msgstr "création d'une contrainte pour la relation %(linkto)s" msgid "creating EFRDef (EFRDef relation_type ERType %(linkto)s)" -msgstr "création attribut %(linkto)s" +msgstr "création d'un attribut %(linkto)s" msgid "creating ENFRDef (ENFRDef relation_type ERType %(linkto)s)" msgstr "création relation %(linkto)s" @@ -1744,12 +1753,18 @@ msgid "february" msgstr "février" +msgid "file tree view" +msgstr "arborescence (fichiers)" + msgid "final" msgstr "final" msgid "firstname" msgstr "prénom" +msgid "foaf" +msgstr "foaf" + msgid "follow" msgstr "suivre le lien" @@ -2091,6 +2106,9 @@ msgid "list" msgstr "liste" +msgid "loading" +msgstr "chargement" + msgid "log in" msgstr "s'identifier" @@ -2119,6 +2137,9 @@ msgid "manage bookmarks" msgstr "gérer les signets" +msgid "manage permissions" +msgstr "gestion des permissions" + msgid "manage security" msgstr "gestion de la sécurité" @@ -2233,6 +2254,9 @@ msgid "not authorized" msgstr "non autorisé" +msgid "not selected" +msgstr "non sélectionné" + msgid "not specified" msgstr "non spécifié" @@ -2269,6 +2293,15 @@ msgid "ordernum" msgstr "ordre" +msgid "owl (tbox+abox)" +msgstr "" + +msgid "owlabox" +msgstr "" + +msgid "owlaboxlight" +msgstr "" + msgid "owned_by" msgstr "appartient à" @@ -2522,6 +2555,9 @@ msgid "select this entity" msgstr "sélectionner cette entité" +msgid "selected" +msgstr "sélectionné" + msgid "semantic description of this attribute" msgstr "description sémantique de cet attribut" @@ -2650,6 +2686,9 @@ msgid "task progression" msgstr "avancement de la tâche" +msgid "tbox" +msgstr "" + msgid "text" msgstr "text" @@ -2727,6 +2766,9 @@ msgid "transition_of_object" msgstr "a pour transition" +msgid "tree view" +msgstr "arborescence" + msgid "tuesday" msgstr "mardi" diff -r a81d3babb582 -r 85eae6a79f75 interfaces.py --- a/interfaces.py Thu Jan 15 10:13:25 2009 +0100 +++ b/interfaces.py Wed Jun 03 19:42:23 2009 +0200 @@ -238,3 +238,7 @@ def rss_feed_url(self): """return an url which layout sub-entities item """ +class ISIOC(Interface): + """interface for entities with sioc views""" + + diff -r a81d3babb582 -r 85eae6a79f75 rset.py --- a/rset.py Thu Jan 15 10:13:25 2009 +0100 +++ b/rset.py Wed Jun 03 19:42:23 2009 +0200 @@ -1,7 +1,7 @@ """The `ResultSet` class which is returned as result of a rql query :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -60,11 +60,15 @@ def __repr__(self): if not self.rows: return '' % self.rql + rows = self.rows + if len(rows) > 10: + rows = rows[:10] + ['...'] if not self.description: - return '' % (self.rql, '\n'.join(str(r) for r in self.rows)) - return '' % (self.rql, - '\n'.join('%s (%s)' % (r, d) - for r, d in zip(self.rows, self.description))) + return '' % (self.rql, len(self.rows), + '\n'.join(str(r) for r in rows)) + return '' % (self.rql, len(self.rows), + '\n'.join('%s (%s)' % (r, d) + for r, d in zip(rows, self.description))) @cached def possible_actions(self): @@ -464,7 +468,8 @@ rqlst = self.syntax_tree() etype = self.description[row][col] if self.vreg.schema.eschema(etype).is_final(): - # final type, find a better (ambiguous) one + # final type, find a better one to locate the correct subquery + # (ambiguous if possible) for i in xrange(len(rqlst.children[0].selection)): if i == col: continue @@ -476,18 +481,17 @@ locate_query_col = i if len(self.column_types(i)) > 1: break - # UNION query, find the subquery from which this entity has been - # found + # UNION query, find the subquery from which this entity has been found select = rqlst.locate_subquery(locate_query_col, etype, self.args) try: myvar = select.selection[col].variable except AttributeError: - # no .selection attribute is available + # not a variable return None, None rel = myvar.main_relation() if rel is not None: index = rel.children[0].variable.selected_index() - if index is not None: + if index is not None and self.rows[row][index]: return self.get_entity(row, index), rel.r_type return None, None diff -r a81d3babb582 -r 85eae6a79f75 schema.py --- a/schema.py Thu Jan 15 10:13:25 2009 +0100 +++ b/schema.py Wed Jun 03 19:42:23 2009 +0200 @@ -1,7 +1,7 @@ """classes to define schemas for CubicWeb :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" diff -r a81d3babb582 -r 85eae6a79f75 server/__init__.py --- a/server/__init__.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/__init__.py Wed Jun 03 19:42:23 2009 +0200 @@ -113,6 +113,8 @@ for eid, etype in needisfix: handler.session.unsafe_execute('SET X is E WHERE X eid %(x)s, E name %(name)s', {'x': eid, 'name': etype}, 'x') + handler.session.unsafe_execute('SET X is_instance_of E WHERE X eid %(x)s, E name %(name)s', + {'x': eid, 'name': etype}, 'x') # insert versions handler.cmd_add_entity('EProperty', pkey=u'system.version.cubicweb', value=unicode(config.cubicweb_version())) diff -r a81d3babb582 -r 85eae6a79f75 server/hooks.py --- a/server/hooks.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/hooks.py Wed Jun 03 19:42:23 2009 +0200 @@ -2,7 +2,7 @@ entities... :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" diff -r a81d3babb582 -r 85eae6a79f75 server/migractions.py --- a/server/migractions.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/migractions.py Wed Jun 03 19:42:23 2009 +0200 @@ -11,7 +11,7 @@ :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -264,13 +264,15 @@ self.commit() def cmd_add_cube(self, cube, update_database=True): + self.cmd_add_cubes( (cube,), update_database) + + def cmd_add_cubes(self, cubes, update_database=True): """update_database is telling if the database schema should be updated or if only the relevant eproperty should be inserted (for the case where a cube has been extracted from an existing application, so the cube schema is already in there) """ - newcubes = super(ServerMigrationHelper, self).cmd_add_cube( - cube) + newcubes = super(ServerMigrationHelper, self).cmd_add_cubes(cubes) if not newcubes: return for pack in newcubes: @@ -279,22 +281,22 @@ if not update_database: self.commit() return - self.new_schema = self.config.load_schema() + with_new_cubes = self.config.load_schema() new = set() # execute pre-create files for pack in reversed(newcubes): self.exec_event_script('precreate', self.config.cube_dir(pack)) # add new entity and relation types - for rschema in self.new_schema.relations(): + for rschema in with_new_cubes.relations(): if not rschema in self.repo.schema: self.cmd_add_relation_type(rschema.type) new.add(rschema.type) - for eschema in self.new_schema.entities(): + for eschema in with_new_cubes.entities(): if not eschema in self.repo.schema: self.cmd_add_entity_type(eschema.type) new.add(eschema.type) # check if attributes has been added to existing entities - for rschema in self.new_schema.relations(): + for rschema in with_new_cubes.relations(): existingschema = self.repo.schema.rschema(rschema.type) for (fromtype, totype) in rschema.iter_rdefs(): if existingschema.has_rdef(fromtype, totype): @@ -679,11 +681,11 @@ espschema = eschema.specializes() if repospschema and not espschema: self.rqlexec('DELETE X specializes Y WHERE X is EEType, X name %(x)s', - {'x': str(repoechema)}) + {'x': str(repoeschema)}) elif not repospschema and espschema: self.rqlexec('SET X specializes Y WHERE X is EEType, X name %(x)s, ' 'Y is EEType, Y name %(y)s', - {'x': str(repoechema), 'y': str(epschema)}) + {'x': str(repoeschema), 'y': str(espschema)}) self.rqlexecall(ss.updateeschema2rql(eschema), ask_confirm=self.verbosity >= 2) for rschema, targettypes, x in eschema.relation_definitions(True): diff -r a81d3babb582 -r 85eae6a79f75 server/msplanner.py --- a/server/msplanner.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/msplanner.py Wed Jun 03 19:42:23 2009 +0200 @@ -288,17 +288,21 @@ flag = 0 for v in crossvars: if isinstance(v, Constant): - self._sourcesvars[ssource][v] = set(self._solindices) + allsols = set(self._solindices) + try: + self._sourcesvars[ssource][v] = allsols + except KeyError: + self._sourcesvars[ssource] = {v: allsols} if len(vsources[v]) == 1: if iter(vsources[v]).next()[0].uri == 'system': flag = 1 for ov in crossvars: - if ov is not v and ov._q_invariant: + if ov is not v and (isinstance(ov, Constant) or ov._q_invariant): ssset = frozenset((ssource,)) self._remove_sources(ov, vsources[ov] - ssset) else: for ov in crossvars: - if ov is not v and ov._q_invariant: + if ov is not v and (isinstance(ov, Constant) or ov._q_invariant): needsplit = False break else: @@ -661,7 +665,7 @@ if not var.scope is self.rqlst: if isinstance(var, Variable): return var, sourcevars.pop(var) - secondchoice = var, sourcevars.pop(var) + secondchoice = var else: # priority to variable outer scope for var in sourcevars: @@ -995,7 +999,9 @@ step = AggrStep(plan, selection, select, atemptable, temptable) step.children = steps elif len(steps) > 1: - if select.need_intersect: + if select.need_intersect or any(select.need_intersect + for step in steps + for select in step.union.children): if temptable: step = IntersectFetchStep(plan) else: diff -r a81d3babb582 -r 85eae6a79f75 server/mssteps.py --- a/server/mssteps.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/mssteps.py Wed Jun 03 19:42:23 2009 +0200 @@ -6,7 +6,7 @@ for now) :organization: Logilab -:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -274,9 +274,9 @@ result &= frozenset(step.execute()) result = list(result) if self.offset: - result = result[offset:] + result = result[self.offset:] if self.limit: - result = result[:limit] + result = result[:self.limit] return result diff -r a81d3babb582 -r 85eae6a79f75 server/querier.py --- a/server/querier.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/querier.py Wed Jun 03 19:42:23 2009 +0200 @@ -196,7 +196,7 @@ self._insert_security(union, noinvariant) self.rqlhelper.simplify(union) self.sqlannotate(union) - set_qdata(union, noinvariant) + set_qdata(self.schema.rschema, union, noinvariant) if union.has_text_query: self.cache_key = None diff -r a81d3babb582 -r 85eae6a79f75 server/repository.py --- a/server/repository.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/repository.py Wed Jun 03 19:42:23 2009 +0200 @@ -11,7 +11,7 @@ :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -490,7 +490,7 @@ session = self.internal_session() try: if session.execute('EUser X WHERE X login %(login)s', {'login': login}): - return + return False # we have to create the user user = self.vreg.etype_class('EUser')(session, None) if isinstance(password, unicode): @@ -505,6 +505,7 @@ session.commit() finally: session.close() + return True def connect(self, login, password, cnxprops=None): """open a connection for a given user @@ -791,7 +792,7 @@ # since the current session user may not have required permissions to # do necessary stuff and we don't want to commit user session. # - # More other, even if session is already an internal session but is + # Moreover, even if session is already an internal session but is # processing a commit, we have to use another one if not session.is_internal_session: session = self.internal_session() @@ -803,6 +804,7 @@ entity = source.before_entity_insertion(session, lid, etype, eid) if source.should_call_hooks: self.hm.call_hooks('before_add_entity', etype, session, entity) + # XXX call add_info with complete=False ? self.add_info(session, entity, source, lid) source.after_entity_insertion(session, lid, entity) if source.should_call_hooks: diff -r a81d3babb582 -r 85eae6a79f75 server/rqlannotation.py --- a/server/rqlannotation.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/rqlannotation.py Wed Jun 03 19:42:23 2009 +0200 @@ -20,7 +20,7 @@ #if server.DEBUG: # print '-------- sql annotate', repr(rqlst) getrschema = annotator.schema.rschema - has_text_query = need_intersect = False + has_text_query = False need_distinct = rqlst.distinct for rel in rqlst.iget_nodes(Relation): if rel.neged(strict=True): @@ -29,13 +29,6 @@ else: rschema = getrschema(rel.r_type) if not rschema.is_final(): - # if one of the relation's variable is ambiguous, an intersection - # will be necessary - for vref in rel.get_nodes(VariableRef): - var = vref.variable - if not var.stinfo['selected'] and len(var.stinfo['possibletypes']) > 1: - need_intersect = True - break if rschema.inlined: try: var = rel.children[1].children[0].variable @@ -147,23 +140,23 @@ except CantSelectPrincipal: stinfo['invariant'] = False rqlst.need_distinct = need_distinct - rqlst.need_intersect = need_intersect return has_text_query class CantSelectPrincipal(Exception): pass -def _select_principal(sqlscope, relations): +def _select_principal(sqlscope, relations, _sort=lambda x:x): """given a list of rqlst relations, select one which will be used to represent an invariant variable (e.g. using on extremity of the relation instead of the variable's type table """ + # _sort argument is there for test diffscope_rels = {} has_same_scope_rel = False ored_rels = set() diffscope_rels = set() - for rel in relations: + for rel in _sort(relations): # note: only eid and has_text among all final relations may be there if rel.r_type in ('eid', 'identity'): has_same_scope_rel = rel.sqlscope is sqlscope @@ -183,18 +176,17 @@ if isinstance(common_parent(rel1, rel2), Or): ored_rels.discard(rel1) ored_rels.discard(rel2) - for rel in ored_rels: + for rel in _sort(ored_rels): if rel.sqlscope is sqlscope: return rel diffscope_rels.add(rel) # if DISTINCT query, can use variable from a different scope as principal # since introduced duplicates will be removed if sqlscope.stmt.distinct and diffscope_rels: - return iter(diffscope_rels).next() + return iter(_sort(diffscope_rels)).next() # XXX could use a relation for a different scope if it can't generate # duplicates, so we would have to check cardinality - raise CantSelectPrincipal() - + raise CantSelectPrincipal() def _select_main_var(relations): """given a list of rqlst relations, select one which will be used as main @@ -207,12 +199,12 @@ return principal -def set_qdata(union, noinvariant): +def set_qdata(getrschema, union, noinvariant): """recursive function to set querier data on variables in the syntax tree """ for select in union.children: for subquery in select.with_: - set_qdata(subquery.query, noinvariant) + set_qdata(getrschema, subquery.query, noinvariant) for var in select.defined_vars.itervalues(): if var.stinfo['invariant']: if var in noinvariant and not var.stinfo['principal'].r_type == 'has_text': @@ -221,6 +213,23 @@ var._q_invariant = True else: var._q_invariant = False + for rel in select.iget_nodes(Relation): + if rel.neged(strict=True) and not rel.is_types_restriction(): + rschema = getrschema(rel.r_type) + if not rschema.is_final(): + # if one of the relation's variable is ambiguous but not + # invariant, an intersection will be necessary + for vref in rel.get_nodes(VariableRef): + var = vref.variable + if (not var._q_invariant and var.valuable_references() == 1 + and len(var.stinfo['possibletypes']) > 1): + select.need_intersect = True + break + else: + continue + break + else: + select.need_intersect = False class SQLGenAnnotator(object): diff -r a81d3babb582 -r 85eae6a79f75 server/securityhooks.py --- a/server/securityhooks.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/securityhooks.py Wed Jun 03 19:42:23 2009 +0200 @@ -2,7 +2,7 @@ the user connected to a session :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -54,6 +54,7 @@ entity.check_perm('update') check_entity_attributes(session, entity) except Unauthorized: + entity.clear_local_perm_cache('update') CheckEntityPermissionOp(session, entity=entity, action='update') def before_del_entity(session, eid): diff -r a81d3babb582 -r 85eae6a79f75 server/session.py --- a/server/session.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/session.py Wed Jun 03 19:42:23 2009 +0200 @@ -21,6 +21,18 @@ from cubicweb.common.utils import make_uid from cubicweb.server.rqlrewrite import RQLRewriter +_ETYPE_PYOBJ_MAP = { bool: 'Boolean', + int: 'Int', + long: 'Int', + float: 'Float', + Decimal: 'Decimal', + unicode: 'String', + NoneType: None, + Binary: 'Bytes', + DateTimeType: 'Datetime', + DateTimeDeltaType: 'Interval', + } + def etype_from_pyobj(value): """guess yams type from python value""" # note: @@ -28,17 +40,7 @@ # * use type(value) and not value.__class__ since mx instances have no # __class__ attribute # * XXX Date, Time - return {bool: 'Boolean', - int: 'Int', - long: 'Int', - float: 'Float', - Decimal: 'Decimal', - unicode: 'String', - NoneType: None, - Binary: 'Bytes', - DateTimeType: 'Datetime', - DateTimeDeltaType: 'Interval', - }[type(value)] + return _ETYPE_PYOBJ_MAP[type(value)] def is_final(rqlst, variable, args): # try to find if this is a final var or not @@ -58,35 +60,8 @@ description.append(term.get_type(solution, args)) return description -#XXX rql <= 0.18.3 bw compat from rql import stmts -if not hasattr(stmts.Union, 'get_variable_variables'): - def _union_get_variable_variables(self): - """return the set of variable names which take different type according to - the solution - """ - change = set() - values = {} - for select in self.children: - change.update(select.get_variable_variables(values)) - return change - stmts.Union.get_variable_variables = _union_get_variable_variables - - def _select_get_variable_variables(self, _values=None): - """return the set of variable names which take different type according to - the solution - """ - change = set() - if _values is None: - _values = {} - for solution in self.solutions: - for vname, etype in solution.iteritems(): - if not vname in _values: - _values[vname] = etype - elif _values[vname] != etype: - change.add(vname) - return change - stmts.Select.get_variable_variables = _select_get_variable_variables +assert hasattr(stmts.Union, 'get_variable_variables'), "You need RQL > 0.18.3" class Session(RequestSessionMixIn): """tie session id, user, connections pool and other session data all diff -r a81d3babb582 -r 85eae6a79f75 server/sources/native.py --- a/server/sources/native.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/sources/native.py Wed Jun 03 19:42:23 2009 +0200 @@ -188,7 +188,7 @@ rqlst.restricted_vars = () rqlst.children[0].solutions = self._sols self.repo.querier.sqlgen_annotate(rqlst) - set_qdata(rqlst, ()) + set_qdata(self.schema.rschema, rqlst, ()) return rqlst def set_schema(self, schema): diff -r a81d3babb582 -r 85eae6a79f75 server/sources/pyrorql.py --- a/server/sources/pyrorql.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/sources/pyrorql.py Wed Jun 03 19:42:23 2009 +0200 @@ -159,7 +159,8 @@ external repository """ self.info('synchronizing pyro source %s', self.uri) - extrepo = self.get_connection()._repo + cnx = self.get_connection() + extrepo = cnx._repo etypes = self.support_entities.keys() if mtime is None: mtime = self.last_update_time() @@ -170,11 +171,13 @@ try: for etype, extid in modified: try: - eid = self.extid2eid(extid, etype, session) - rset = session.eid_rset(eid, etype) - entity = rset.get_entity(0, 0) - entity.complete(entity.e_schema.indexable_attributes()) - repo.index_entity(session, entity) + exturi = cnx.describe(extid)[1] + if exturi == 'system' or not exturi in repo.sources_by_uri: + eid = self.extid2eid(extid, etype, session) + rset = session.eid_rset(eid, etype) + entity = rset.get_entity(0, 0) + entity.complete(entity.e_schema.indexable_attributes()) + repo.index_entity(session, entity) except: self.exception('while updating %s with external id %s of source %s', etype, extid, self.uri) @@ -277,18 +280,28 @@ descr = rset.description if rset: needtranslation = [] + rows = rset.rows for i, etype in enumerate(descr[0]): if (etype is None or not self.schema.eschema(etype).is_final() or getattr(union.locate_subquery(i, etype, args).selection[i], 'uidtype', None)): needtranslation.append(i) if needtranslation: - for rowindex, row in enumerate(rset): + cnx = session.pool.connection(self.uri) + for rowindex in xrange(rset.rowcount - 1, -1, -1): + row = rows[rowindex] for colindex in needtranslation: if row[colindex] is not None: # optional variable etype = descr[rowindex][colindex] - eid = self.extid2eid(row[colindex], etype, session) - row[colindex] = eid - results = rset.rows + exttype, exturi, extid = cnx.describe(row[colindex]) + if exturi == 'system' or not exturi in self.repo.sources_by_uri: + eid = self.extid2eid(row[colindex], etype, session) + row[colindex] = eid + else: + # skip this row + del rows[rowindex] + del descr[rowindex] + break + results = rows else: results = [] if server.DEBUG: diff -r a81d3babb582 -r 85eae6a79f75 server/sources/rql2sql.py --- a/server/sources/rql2sql.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/sources/rql2sql.py Wed Jun 03 19:42:23 2009 +0200 @@ -488,7 +488,8 @@ sql.insert(1, 'FROM (SELECT 1) AS _T') sqls.append('\n'.join(sql)) if select.need_intersect: - if distinct: + # XXX use getattr for lgc bw compat, remove once 0.37.3 is out + if distinct or not getattr(self.dbms_helper, 'intersect_all_support', True): return '\nINTERSECT\n'.join(sqls) else: return '\nINTERSECT ALL\n'.join(sqls) diff -r a81d3babb582 -r 85eae6a79f75 server/test/data/sources_multi --- a/server/test/data/sources_multi Thu Jan 15 10:13:25 2009 +0100 +++ b/server/test/data/sources_multi Wed Jun 03 19:42:23 2009 +0200 @@ -15,6 +15,13 @@ cubicweb-password = gingkow mapping-file = extern_mapping.py +[extern-multi] +adapter = pyrorql +pyro-ns-id = extern-multi +cubicweb-user = admin +cubicweb-password = gingkow +mapping-file = extern_mapping.py + [admin] login = admin password = gingkow diff -r a81d3babb582 -r 85eae6a79f75 server/test/unittest_migractions.py --- a/server/test/unittest_migractions.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/test/unittest_migractions.py Wed Jun 03 19:42:23 2009 +0200 @@ -134,7 +134,7 @@ self.assertEquals([str(rs) for rs in self.schema['Folder2'].object_relations()], ['filed_under2', 'identity']) self.assertEquals(sorted(str(e) for e in self.schema['filed_under2'].subjects()), - ['Affaire', 'Card', 'Division', 'ECache', 'Email', 'EmailThread', 'File', + ['Affaire', 'Card', 'Division', 'Email', 'EmailThread', 'File', 'Folder2', 'Image', 'Note', 'Personne', 'Societe', 'SubDivision']) self.assertEquals(self.schema['filed_under2'].objects(), ('Folder2',)) eschema = self.schema.eschema('Folder2') @@ -161,7 +161,7 @@ self.mh.cmd_add_relation_type('filed_under2') self.failUnless('filed_under2' in self.schema) self.assertEquals(sorted(str(e) for e in self.schema['filed_under2'].subjects()), - ['Affaire', 'Card', 'Division', 'ECache', 'Email', 'EmailThread', 'File', + ['Affaire', 'Card', 'Division', 'Email', 'EmailThread', 'File', 'Folder2', 'Image', 'Note', 'Personne', 'Societe', 'SubDivision']) self.assertEquals(self.schema['filed_under2'].objects(), ('Folder2',)) @@ -364,23 +364,36 @@ def test_add_remove_cube(self): cubes = set(self.config.cubes()) schema = self.repo.schema + self.assertEquals(sorted(schema['see_also']._rproperties.keys()), + sorted([('EmailThread', 'EmailThread'), ('Folder', 'Folder'), + ('Bookmark', 'Bookmark'), ('Bookmark', 'Note'), + ('Note', 'Note'), ('Note', 'Bookmark')])) try: - self.mh.cmd_remove_cube('email') - # file was there because it's an email dependancy, should have been removed - cubes.remove('email') - cubes.remove('file') - self.assertEquals(set(self.config.cubes()), cubes) - for ertype in ('Email', 'EmailThread', 'EmailPart', 'File', 'Image', - 'sender', 'in_thread', 'reply_to', 'data_format'): - self.failIf(ertype in schema, ertype) - self.assertEquals(sorted(schema['see_also']._rproperties.keys()), - [('Folder', 'Folder')]) - self.assertEquals(schema['see_also'].subjects(), ('Folder',)) - self.assertEquals(schema['see_also'].objects(), ('Folder',)) - self.assertEquals(self.execute('Any X WHERE X pkey "system.version.email"').rowcount, 0) - self.assertEquals(self.execute('Any X WHERE X pkey "system.version.file"').rowcount, 0) - self.failIf('email' in self.config.cubes()) - self.failIf('file' in self.config.cubes()) + try: + self.mh.cmd_remove_cube('email') + # file was there because it's an email dependancy, should have been removed + cubes.remove('email') + cubes.remove('file') + self.assertEquals(set(self.config.cubes()), cubes) + for ertype in ('Email', 'EmailThread', 'EmailPart', 'File', 'Image', + 'sender', 'in_thread', 'reply_to', 'data_format'): + self.failIf(ertype in schema, ertype) + self.assertEquals(sorted(schema['see_also']._rproperties.keys()), + sorted([('Folder', 'Folder'), + ('Bookmark', 'Bookmark'), + ('Bookmark', 'Note'), + ('Note', 'Note'), + ('Note', 'Bookmark')])) + self.assertEquals(sorted(schema['see_also'].subjects()), ['Bookmark', 'Folder', 'Note']) + self.assertEquals(sorted(schema['see_also'].objects()), ['Bookmark', 'Folder', 'Note']) + self.assertEquals(self.execute('Any X WHERE X pkey "system.version.email"').rowcount, 0) + self.assertEquals(self.execute('Any X WHERE X pkey "system.version.file"').rowcount, 0) + self.failIf('email' in self.config.cubes()) + self.failIf('file' in self.config.cubes()) + except : + import traceback + traceback.print_exc() + raise finally: self.mh.cmd_add_cube('email') cubes.add('email') @@ -390,9 +403,13 @@ 'sender', 'in_thread', 'reply_to', 'data_format'): self.failUnless(ertype in schema, ertype) self.assertEquals(sorted(schema['see_also']._rproperties.keys()), - [('EmailThread', 'EmailThread'), ('Folder', 'Folder')]) - self.assertEquals(sorted(schema['see_also'].subjects()), ['EmailThread', 'Folder']) - self.assertEquals(sorted(schema['see_also'].objects()), ['EmailThread', 'Folder']) + sorted([('EmailThread', 'EmailThread'), ('Folder', 'Folder'), + ('Bookmark', 'Bookmark'), + ('Bookmark', 'Note'), + ('Note', 'Note'), + ('Note', 'Bookmark')])) + self.assertEquals(sorted(schema['see_also'].subjects()), ['Bookmark', 'EmailThread', 'Folder', 'Note']) + self.assertEquals(sorted(schema['see_also'].objects()), ['Bookmark', 'EmailThread', 'Folder', 'Note']) from cubes.email.__pkginfo__ import version as email_version from cubes.file.__pkginfo__ import version as file_version self.assertEquals(self.execute('Any V WHERE X value V, X pkey "system.version.email"')[0][0], diff -r a81d3babb582 -r 85eae6a79f75 server/test/unittest_multisources.py --- a/server/test/unittest_multisources.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/test/unittest_multisources.py Wed Jun 03 19:42:23 2009 +0200 @@ -10,10 +10,13 @@ sourcefile = 'sources_multi' -class ExternalSourceConfiguration(TestServerConfiguration): +class ExternalSource1Configuration(TestServerConfiguration): sourcefile = 'sources_extern' + +class ExternalSource2Configuration(TestServerConfiguration): + sourcefile = 'sources_multi2' -repo2, cnx2 = init_test_database('sqlite', config=ExternalSourceConfiguration('data')) +repo2, cnx2 = init_test_database('sqlite', config=ExternalSource1Configuration('data')) cu = cnx2.cursor() ec1 = cu.execute('INSERT Card X: X title "C3: An external card", X wikiid "aaa"')[0][0] cu.execute('INSERT Card X: X title "C4: Ze external card", X wikiid "zzz"') @@ -22,9 +25,11 @@ MTIME = now() - 0.1 +repo3, cnx3 = init_test_database('sqlite', config=ExternalSource2Configuration('data')) + # XXX, access existing connection, no pyro connection from cubicweb.server.sources.pyrorql import PyroRQLSource -PyroRQLSource.get_connection = lambda x: cnx2 +PyroRQLSource.get_connection = lambda x: x.uri == 'extern-multi' and cnx3 or cnx2 # necessary since the repository is closing its initial connections pool though # we want to keep cnx2 valid from cubicweb.dbapi import Connection @@ -84,12 +89,12 @@ self.assertEquals(rset.rows, rsetbase.rows[2:4]) def test_has_text(self): - self.repo.sources[-1].synchronize(MTIME) # in case fti_update has been run before + self.repo.sources_by_uri['extern'].synchronize(MTIME) # in case fti_update has been run before self.failUnless(self.execute('Any X WHERE X has_text "affref"')) self.failUnless(self.execute('Affaire X WHERE X has_text "affref"')) def test_anon_has_text(self): - self.repo.sources[-1].synchronize(MTIME) # in case fti_update has been run before + self.repo.sources_by_uri['extern'].synchronize(MTIME) # in case fti_update has been run before self.execute('INSERT Affaire X: X ref "no readable card"')[0][0] aff1 = self.execute('INSERT Affaire X: X ref "card"')[0][0] # grant read access @@ -98,7 +103,7 @@ cnx = self.login('anon') cu = cnx.cursor() rset = cu.execute('Any X WHERE X has_text "card"') - self.assertEquals(len(rset), 5) + self.assertEquals(len(rset), 5, zip(rset.rows, rset.description)) def test_synchronization(self): cu = cnx2.cursor() @@ -107,13 +112,14 @@ cnx2.commit() try: # force sync - self.repo.sources[-1].synchronize(MTIME) + self.repo.sources_by_uri['extern'].synchronize(MTIME) self.failUnless(self.execute('Any X WHERE X has_text "blah"')) self.failUnless(self.execute('Any X WHERE X has_text "affreux"')) cu.execute('DELETE Affaire X WHERE X eid %(x)s', {'x': aff2}) cnx2.commit() - self.repo.sources[-1].synchronize(MTIME) - self.failIf(self.execute('Any X WHERE X has_text "affreux"')) + self.repo.sources_by_uri['extern'].synchronize(MTIME) + rset = self.execute('Any X WHERE X has_text "affreux"') + self.failIf(rset) finally: # restore state cu.execute('SET X ref "AFFREF" WHERE X eid %(x)s', {'x': aff1}, 'x') @@ -139,7 +145,8 @@ self.execute('Any X ORDERBY DUMB_SORT(RF) WHERE X title RF') def test_in_eid(self): - iec1 = self.repo.extid2eid(self.repo.sources[-1], ec1, 'Card', self.session) + iec1 = self.repo.extid2eid(self.repo.sources_by_uri['extern'], ec1, + 'Card', self.session) rset = self.execute('Any X WHERE X eid IN (%s, %s)' % (iec1, self.ic1)) self.assertEquals(sorted(r[0] for r in rset.rows), sorted([iec1, self.ic1])) diff -r a81d3babb582 -r 85eae6a79f75 server/test/unittest_querier.py --- a/server/test/unittest_querier.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/test/unittest_querier.py Wed Jun 03 19:42:23 2009 +0200 @@ -454,7 +454,7 @@ 'WHERE RT name N, RDEF relation_type RT ' 'HAVING COUNT(RDEF) > 10') self.assertListEquals(rset.rows, - [[u'description', 11], ['in_basket', 12], + [[u'description', 11], ['in_basket', 11], [u'name', 13], [u'created_by', 33], [u'creation_date', 33], [u'is', 33], [u'is_instance_of', 33], [u'modification_date', 33], [u'owned_by', 33]]) diff -r a81d3babb582 -r 85eae6a79f75 server/test/unittest_rql2sql.py --- a/server/test/unittest_rql2sql.py Thu Jan 15 10:13:25 2009 +0100 +++ b/server/test/unittest_rql2sql.py Wed Jun 03 19:42:23 2009 +0200 @@ -156,11 +156,6 @@ ] ADVANCED= [ - ('Any X WHERE X is ET, ET eid 2', - '''SELECT rel_is0.eid_from -FROM is_relation AS rel_is0 -WHERE rel_is0.eid_to=2'''), - ("Societe S WHERE S nom 'Logilab' OR S nom 'Caesium'", '''SELECT S.eid @@ -810,7 +805,7 @@ 'F name "read", F require_group E, U in_group E)), U eid 1', '''SELECT A.eid, rel_documented_by0.eid_to FROM Affaire AS A LEFT OUTER JOIN documented_by_relation AS rel_documented_by0 ON (rel_documented_by0.eid_from=A.eid) -WHERE ((rel_documented_by0.eid_to IS NULL) OR (EXISTS(SELECT 1 FROM require_permission_relation AS rel_require_permission1, EPermission AS F, require_group_relation AS rel_require_group2, in_group_relation AS rel_in_group3 WHERE rel_documented_by0.eid_to=rel_require_permission1.eid_from AND rel_require_permission1.eid_to=F.eid AND F.name=read AND rel_require_group2.eid_from=F.eid AND rel_in_group3.eid_from=1 AND rel_in_group3.eid_to=rel_require_group2.eid_to)))'''), +WHERE ((rel_documented_by0.eid_to IS NULL) OR (EXISTS(SELECT 1 FROM require_permission_relation AS rel_require_permission1, EPermission AS F, require_group_relation AS rel_require_group2, in_group_relation AS rel_in_group3 WHERE rel_documented_by0.eid_to=rel_require_permission1.eid_from AND rel_require_permission1.eid_to=F.eid AND F.name=read AND rel_require_group2.eid_from=F.eid AND rel_in_group3.eid_to=rel_require_group2.eid_to AND rel_in_group3.eid_from=1)))'''), ("Any X WHERE X eid 12, P? connait X", '''SELECT X.eid @@ -1064,7 +1059,57 @@ ] +INTERSECT = [ + ('Any SN WHERE NOT X in_state S, S name SN', + '''SELECT DISTINCT S.name +FROM Affaire AS X, State AS S +WHERE (X.in_state IS NULL OR X.in_state!=S.eid) +INTERSECT +SELECT DISTINCT S.name +FROM EUser AS X, State AS S +WHERE (X.in_state IS NULL OR X.in_state!=S.eid) +INTERSECT +SELECT DISTINCT S.name +FROM Note AS X, State AS S +WHERE (X.in_state IS NULL OR X.in_state!=S.eid)'''), + ('Any PN WHERE NOT X travaille S, X nom PN, S is IN(Division, Societe)', + '''SELECT X.nom +FROM Personne AS X +WHERE NOT EXISTS(SELECT 1 FROM travaille_relation AS rel_travaille0,Division AS S WHERE rel_travaille0.eid_from=X.eid AND rel_travaille0.eid_to=S.eid) +INTERSECT ALL +SELECT X.nom +FROM Personne AS X +WHERE NOT EXISTS(SELECT 1 FROM travaille_relation AS rel_travaille0,Societe AS S WHERE rel_travaille0.eid_from=X.eid AND rel_travaille0.eid_to=S.eid)'''), + + ('Any PN WHERE NOT X travaille S, S nom PN, S is IN(Division, Societe)', + '''SELECT S.nom +FROM Division AS S +WHERE NOT EXISTS(SELECT 1 FROM travaille_relation AS rel_travaille0 WHERE rel_travaille0.eid_to=S.eid) +UNION ALL +SELECT S.nom +FROM Societe AS S +WHERE NOT EXISTS(SELECT 1 FROM travaille_relation AS rel_travaille0 WHERE rel_travaille0.eid_to=S.eid)'''), + + ('Personne X WHERE NOT X travaille S, S nom "chouette"', + '''SELECT X.eid +FROM Division AS S, Personne AS X +WHERE NOT EXISTS(SELECT 1 FROM travaille_relation AS rel_travaille0 WHERE rel_travaille0.eid_from=X.eid AND rel_travaille0.eid_to=S.eid) AND S.nom=chouette +UNION ALL +SELECT X.eid +FROM Personne AS X, Societe AS S +WHERE NOT EXISTS(SELECT 1 FROM travaille_relation AS rel_travaille0 WHERE rel_travaille0.eid_from=X.eid AND rel_travaille0.eid_to=S.eid) AND S.nom=chouette +UNION ALL +SELECT X.eid +FROM Personne AS X, SubDivision AS S +WHERE NOT EXISTS(SELECT 1 FROM travaille_relation AS rel_travaille0 WHERE rel_travaille0.eid_from=X.eid AND rel_travaille0.eid_to=S.eid) AND S.nom=chouette'''), + + ('Any X WHERE X is ET, ET eid 2', + '''SELECT rel_is0.eid_from +FROM is_relation AS rel_is0 +WHERE rel_is0.eid_to=2'''), + + ] from logilab.common.adbh import ADV_FUNC_HELPER_DIRECTORY class PostgresSQLGeneratorTC(RQLGeneratorTC): @@ -1196,6 +1241,10 @@ def test_negation(self): for t in self._parse(NEGATIONS): yield t + + def test_intersection(self): + for t in self._parse(INTERSECT): + yield t def test_union(self): for t in self._parse(( @@ -1370,7 +1419,7 @@ self.o = SQLGenerator(schema, dbms_helper) def _norm_sql(self, sql): - return sql.strip().replace(' ILIKE ', ' LIKE ') + return sql.strip().replace(' ILIKE ', ' LIKE ').replace('\nINTERSECT ALL\n', '\nINTERSECT\n') def test_union(self): for t in self._parse(( diff -r a81d3babb582 -r 85eae6a79f75 skeleton/__pkginfo__.py.tmpl --- a/skeleton/__pkginfo__.py.tmpl Thu Jan 15 10:13:25 2009 +0100 +++ b/skeleton/__pkginfo__.py.tmpl Wed Jun 03 19:42:23 2009 +0200 @@ -1,6 +1,7 @@ # pylint: disable-msg=W0622 """%(distname)s application packaging information""" +modname = '%(cubename)s' distname = '%(distname)s' numversion = (0, 1, 0) @@ -10,8 +11,8 @@ copyright = '''Copyright (c) %(year)s %(author)s. %(author-web-site)s -- mailto:%(author-email)s''' -author = %(author)s' -author_email = %(author-email)s' +author = '%(author)s' +author_email = '%(author-email)s' short_desc = '%(shortdesc)s' long_desc = '''%(longdesc)s''' diff -r a81d3babb582 -r 85eae6a79f75 test/unittest_cwconfig.py --- a/test/unittest_cwconfig.py Thu Jan 15 10:13:25 2009 +0100 +++ b/test/unittest_cwconfig.py Wed Jun 03 19:42:23 2009 +0200 @@ -8,7 +8,7 @@ def unabsolutize(path): parts = path.split(os.sep) - for i, part in enumerate(parts): + for i, part in reversed(tuple(enumerate(parts))): if part in ('cubicweb', 'cubes', 'cubes'): return '/'.join(parts[i+1:]) raise Exception('duh? %s' % path) diff -r a81d3babb582 -r 85eae6a79f75 test/unittest_rset.py --- a/test/unittest_rset.py Thu Jan 15 10:13:25 2009 +0100 +++ b/test/unittest_rset.py Wed Jun 03 19:42:23 2009 +0200 @@ -300,7 +300,13 @@ attr = etype == 'Bookmark' and 'title' or 'name' self.assertEquals(entity[attr], n) - + def test_related_entity_optional(self): + e = self.add_entity('Bookmark', title=u'aaaa', path=u'path') + rset = self.execute('Any B,U,L WHERE B bookmarked_by U?, U login L') + entity, rtype = rset.related_entity(0, 2) + self.assertEquals(entity, None) + self.assertEquals(rtype, None) + def test_related_entity_union_subquery(self): e = self.add_entity('Bookmark', title=u'aaaa', path=u'path') rset = self.execute('Any X,N ORDERBY N WITH X,N BEING ' diff -r a81d3babb582 -r 85eae6a79f75 toolsutils.py --- a/toolsutils.py Thu Jan 15 10:13:25 2009 +0100 +++ b/toolsutils.py Wed Jun 03 19:42:23 2009 +0200 @@ -273,7 +273,7 @@ }), ("host", {'short': 'H', 'type' : 'string', 'metavar': '', - 'default': 'all-in-one', + 'default': None, 'help': 'specify the name server\'s host name. Will be detected by \ broadcast if not provided.', }), diff -r a81d3babb582 -r 85eae6a79f75 vregistry.py --- a/vregistry.py Thu Jan 15 10:13:25 2009 +0100 +++ b/vregistry.py Wed Jun 03 19:42:23 2009 +0200 @@ -20,7 +20,7 @@ :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -105,7 +105,10 @@ "can't be used together") if '__select__' not in classdict and '__selectors__' in classdict: selectors = classdict['__selectors__'] - classdict['__select__'] = classmethod(chainall(*selectors)) + if len(selectors) > 1: + classdict['__select__'] = classmethod(chainall(*selectors)) + else: + classdict['__select__'] = classmethod(selectors[0]) return super(autoselectors, mcs).__new__(mcs, name, bases, classdict) def __setattr__(self, attr, value): diff -r a81d3babb582 -r 85eae6a79f75 web/application.py --- a/web/application.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/application.py Wed Jun 03 19:42:23 2009 +0200 @@ -1,7 +1,7 @@ """CubicWeb web client application object :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -317,6 +317,7 @@ try: ctrlid, rset = self.url_resolver.process(req, path) controller = self.select_controller(ctrlid, req) + req.update_search_state() result = controller.publish(rset=rset) if req.cnx is not None: # req.cnx is None if anonymous aren't allowed and we are diff -r a81d3babb582 -r 85eae6a79f75 web/box.py --- a/web/box.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/box.py Wed Jun 03 19:42:23 2009 +0200 @@ -14,9 +14,9 @@ accepts_registerer, extresources_registerer, etype_rtype_priority_registerer) from cubicweb.common.selectors import ( - etype_rtype_selector, one_line_rset, accept_selector, accept_rtype_selector, - primaryview_selector, contextprop_selector, has_related_entities, - _rqlcondition_selector) + etype_rtype_selector, one_line_rset, accept, has_relation, + primary_view, match_context_prop, has_related_entities, + _rql_condition) from cubicweb.common.view import Template from cubicweb.common.appobject import ReloadableMixIn @@ -42,7 +42,7 @@ box.render(self.w) """ __registry__ = 'boxes' - __selectors__ = Template.__selectors__ + (contextprop_selector,) + __selectors__ = Template.__selectors__ + (match_context_prop,) categories_in_order = () property_defs = { @@ -150,17 +150,15 @@ class EntityBoxTemplate(BoxTemplate): """base class for boxes related to a single entity""" __registerer__ = accepts_registerer - __selectors__ = (one_line_rset, primaryview_selector, - contextprop_selector, etype_rtype_selector, - accept_rtype_selector, accept_selector, - _rqlcondition_selector) + __selectors__ = (one_line_rset, primary_view, + match_context_prop, etype_rtype_selector, + has_relation, accept, _rql_condition) accepts = ('Any',) context = 'incontext' condition = None def call(self, row=0, col=0, **kwargs): - """classes inheriting from EntityBoxTemplate should defined cell_call, - """ + """classes inheriting from EntityBoxTemplate should define cell_call""" self.cell_call(row, col, **kwargs) @@ -185,7 +183,7 @@ class attributes. """ - def cell_call(self, row, col): + def cell_call(self, row, col, view=None): self.req.add_js('cubicweb.ajax.js') entity = self.entity(row, col) box = SideBoxWidget(display_name(self.req, self.rtype), self.id) diff -r a81d3babb582 -r 85eae6a79f75 web/component.py --- a/web/component.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/component.py Wed Jun 03 19:42:23 2009 +0200 @@ -56,7 +56,10 @@ condition = None def call(self, view): - raise RuntimeError() + return self.cell_call(0, 0, view) + + def cell_call(self, row, col, view): + raise NotImplementedError() class NavigationComponent(VComponent): @@ -70,6 +73,19 @@ selected_page_link_templ = u'%s' previous_page_link_templ = next_page_link_templ = page_link_templ no_previous_page_link = no_next_page_link = u'' + + @classmethod + def selected(cls, req, rset, row=None, col=None, page_size=None, **kwargs): + """by default web app objects are usually instantiated on + selection according to a request, a result set, and optional + row and col + """ + instance = super(NavigationComponent, cls).selected(req, rset, row, col, **kwargs) + if page_size is not None: + instance.page_size = page_size + elif 'page_size' in req.form: + instance.page_size = int(req.form['page_size']) + return instance def __init__(self, req, rset): super(NavigationComponent, self).__init__(req, rset) @@ -145,17 +161,17 @@ """ return None - def call(self, view=None): + def cell_call(self, row, col, view=None): rql = self.rql() if rql is None: - entity = self.rset.get_entity(0, 0) + entity = self.rset.get_entity(row, col) if self.target == 'object': role = 'subject' else: role = 'object' rset = entity.related(self.rtype, role) else: - eid = self.rset[0][0] + eid = self.rset[row][col] rset = self.req.execute(self.rql(), {'x': eid}, 'x') if not rset.rowcount: return diff -r a81d3babb582 -r 85eae6a79f75 web/controller.py --- a/web/controller.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/controller.py Wed Jun 03 19:42:23 2009 +0200 @@ -11,7 +11,7 @@ from cubicweb import typed_eid from cubicweb.common.registerers import priority_registerer -from cubicweb.common.selectors import in_group_selector +from cubicweb.common.selectors import match_user_group from cubicweb.common.appobject import AppObject from cubicweb.web import LOGGER, Redirect, RequestError @@ -68,7 +68,7 @@ """ __registry__ = 'controllers' __registerer__ = priority_registerer - __selectors__ = (in_group_selector,) + __selectors__ = (match_user_group,) require_groups = () def __init__(self, *args, **kwargs): diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.ajax.js --- a/web/data/cubicweb.ajax.js Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.ajax.js Wed Jun 03 19:42:23 2009 +0200 @@ -9,6 +9,23 @@ var JSON_BASE_URL = baseuri() + 'json?'; +function postAjaxLoad(node) { + // find sortable tables if there are some + if (typeof(Sortable) != 'undefined') { + Sortable.sortTables(node); + } + // find textareas and wrap them if there are some + if (typeof(FCKeditor) != 'undefined') { + buildWysiwygEditors(node); + } + if (typeof initFacetBoxEvents != 'undefined') { + initFacetBoxEvents(node); + } + if (typeof buildWidgets != 'undefined') { + buildWidgets(node); + } +} + // cubicweb loadxhtml plugin to make jquery handle xhtml response jQuery.fn.loadxhtml = function(url, data, reqtype, mode) { var ajax = null; @@ -40,28 +57,12 @@ } else if (mode == 'append') { jQuery(node).append(domnode); } - // find sortable tables if there are some - if (typeof(Sortable) != 'undefined') { - Sortable.sortTables(node); - } - // find textareas and wrap them if there are some - if (typeof(FCKeditor) != 'undefined') { - buildWysiwygEditors(node); - } - - if (typeof initFacetBoxEvents != 'undefined') { - initFacetBoxEvents(node); - } - - if (typeof buildWidgets != 'undefined') { - buildWidgets(node); - } - + postAjaxLoad(node); while (jQuery.isFunction(callback)) { callback = callback.apply(this, [domnode]); } }); -} +}; @@ -69,7 +70,7 @@ * the associated RQL to build them (Async call) */ function loadDynamicFragments() { - var fragments = getElementsByTagAndClassName('div', 'dynamicFragment'); + var fragments = jQuery('div.dynamicFragment'); if (fragments.length == 0) { return; } @@ -277,7 +278,7 @@ * while the page was generated. */ function unregisterUserCallback(cbname) { - d = async_remote_exec('unregister_user_callback', cbname); + var d = async_remote_exec('unregister_user_callback', cbname); d.addCallback(function() {resetCursor();}); d.addErrback(function(xxx) { updateMessage(_("an error occured")); diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.calendar.css --- a/web/data/cubicweb.calendar.css Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.calendar.css Wed Jun 03 19:42:23 2009 +0200 @@ -74,7 +74,7 @@ } table.omcalendar tr td div.calCellTitle div.stopper { - clear:pos; + /* clear:pos; # FIXME : pos is not a clear value*/ } table.omcalendar tr td { diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.calendar_popup.css --- a/web/data/cubicweb.calendar_popup.css Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.calendar_popup.css Wed Jun 03 19:42:23 2009 +0200 @@ -45,7 +45,7 @@ table.popupCalendar th.prev, table.popupCalendar th.next { - color: orangered; + color: #ff4500; font-size: 50%; font-weight: bold; padding: 2px 0px; diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.css --- a/web/data/cubicweb.css Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.css Wed Jun 03 19:42:23 2009 +0200 @@ -3,10 +3,9 @@ * :copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. * :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr */ - -/******************************************************************************/ -/* main styles */ -/******************************************************************************/ +/***************************************/ +/* xhtml tags styles */ +/***************************************/ *{ margin:0px; @@ -23,14 +22,12 @@ font-family: Verdana, sans-serif; } - h1 { font-size: 188%; margin: 0.2em 0px 0.3em; border-bottom: 1px solid #000; } - h2, h3 { margin-top: 0.2em; margin-bottom: 0.3em; @@ -57,7 +54,7 @@ font-size:105%; } -a, a:active, a:link, a:visited { +a, a:active, a:visited, a:link { color: #ff4500; text-decoration: none; } @@ -66,33 +63,17 @@ text-decoration: underline; } -a.grayedout { - display: block; - padding-left: 2px; - color: #808080; -} - -a.grayedout:visited{ - color: #808080; -} - -a.grayedout:hover { - color: #000; -} - a img { border: none; text-align: center; } p { - margin-top: 0em; - margin-bottom: 0.2em; + margin: 0em 0px 0.2em; padding-top: 2px; } - -table, td, input, select { +table, td, input, select{ font-size: 100%; } @@ -107,7 +88,7 @@ table td img { vertical-align: middle; - margin: 0px 10px 0px 0px; + margin-right: 10px; } ol { @@ -125,29 +106,22 @@ background: url("bullet_orange.png") 0% 6px no-repeat; } -pre { - font-family: Courier, "Courier New", Monaco, monospace; - font-size: 100%; - color: #000; - background-color: #f2f2f2; - border: 1px solid #ccc; - /*overflow:auto; */ +dt { + font-size:1.17em; + font-weight:600; } -blockquote { - font-family: Courier, "Courier New", serif; - font-size: 120%; - margin: 5px 0px; - padding: 0.8em; - background-color: #f2f2f2; - border: 1px solid #ccc; +dd { + margin: 0.6em 0 1.5em 2em; } -note, code { - font-size: 120%; - color: #000; - background-color: #f2f2f2; - border: 1px solid #ccc; +fieldset { + border: none; +} + +legend { + padding: 0px 2px; + font: bold 1em Verdana, sans-serif; } input, textarea { @@ -160,35 +134,41 @@ border: 1px inset #ff7700; } -fieldset { - border: none; -} - -legend { - padding: 0px 2px; - font: bold 1em Verdana, sans-serif; -} - label, .label { - font-weight: bold ! important; + font-weight: bold; } iframe { border: 0px; } -dt { - font-size: 1.17em; - font-weight: 600; +pre { + font-family: Courier, "Courier New", Monaco, monospace; + font-size: 100%; + color: #000; + background-color: #f2f2f2; + border: 1px solid #ccc; } -dd { - margin: 0.6em 0 1.5em 2em; +code { + font-size: 120%; + color: #000; + background-color: #f2f2f2; + border: 1px solid #ccc; } -/******************************************************************************/ -/* generic classes */ -/******************************************************************************/ +blockquote { + font-family: Courier, "Courier New", serif; + font-size: 120%; + margin: 5px 0px; + padding: 0.8em; + background-color: #f2f2f2; + border: 1px solid #ccc; +} + +/***************************************/ +/* generic classes */ +/***************************************/ .odd { background-color: #f7f6f1; @@ -215,77 +195,70 @@ clear: both; } -.noborder { - border: none; -} - -.strong{ - font-weight:bold; -} - -.title { - text-align: left; - font-size: large; - font-weight: bold; -} - -.titleUnderline { - color: #000; - clear: left; - margin: 0px; - padding-top: 0.5em; - border-bottom: 1px solid black; -} - -.helper{ - font-size: 96%; - color: #555544; -} - -.helper:hover { - color: #000; - cursor: default; -} - .hidden { display: none; visibility: hidden; } -.needsvalidation { - font-style: italic; - color: grey ! important; -} -.folder { - /* disable odd/even under folder class */ - background-color: transparent; -} +/* FIXME need to be moved to cubicweb.forms.css */ +li.invisible { list-style: none; background: none; padding: 0px 0px +1px 1px; } -li.invisible { - list-style: none; - background: none; - padding: 0px 0px 1px 1px; -} - +/* FIXME Should use span in html insteed */ li.invisible div { display: inline; } -div.row { - clear: both; - padding-bottom:0.4px + +/***************************************/ +/* LAYOUT */ +/***************************************/ + +/* header */ + +table#header { + background: #ff7700 url("banner.png") left top repeat-x; + text-align: left; +} + +table#header td { + vertical-align: middle; +} + +table#header a { +color: #000; } -div.row span.label{ - padding-right:1em +span#appliName { + font-weight: bold; + color: #000; + white-space: nowrap; +} + +table#header td#headtext { + width: 100%; } -div.field { - margin-left: 0.2em; - display: inline; +a.help{ + display: block; + margin: 0px 5px 0px 8px; + height: 17px; + width: 17px; + background: url('help.png') 0% 0% no-repeat; } +a.help:hover { + background-position: 0px -16px; + text-decoration: none; +} + +/* FIXME appear with 4px width in IE6 */ +div#stateheader{ + min-width: 66%; +} + +/* Popup on login box and userActionBox */ div.popup { position: absolute; z-index: 400; @@ -300,42 +273,7 @@ color: black; } -/******************************************************************************/ -/* header / layout */ -/******************************************************************************/ - -.logo { - background: #fff; - border-left: #f2f2f2; -} - -span#appliName { - font-weight: bold; - color: #000; - white-space: nowrap; -} - -#header { - background: #ff7700 url("banner.png") left top repeat-x; - text-align: left; -} - -table#header td { - vertical-align: middle; -} - -table#header a { -color: #000; -} - -td#headtext { - width: 100%; -} - -/*FIXME appear with 4px width in IE6*/ -div#stateheader{ - width: 66%; -} +/* main zone */ div#page { background: #e2e2e2; @@ -343,16 +281,16 @@ min-height: 800px; } -td#contentcol { - padding: 8px 5px 5px 10px; +table#mainLayout{ + margin:0px 3px; } +table#mainLayout td#contentcol { + padding: 8px 10px 5px; +} -div#pageContent { - clear: both; - padding: 10px 1em 2em; - background: #ffffff; - border: 1px solid #ccc; +table#mainLayout td.navcol { + width: 16em; } #contentheader { @@ -364,157 +302,29 @@ color: #000; } -td.firstcolumn { - width: 220px; -} - -td.navcol { - width: 16em; -} - -div.footer { - text-align: center; -} -div.footer a { - color: #000; - text-decoration: none; +div#pageContent { + clear: both; + padding: 10px 1em 2em; + background: #ffffff; + border: 1px solid #ccc; } -/******************************************************************************/ -/* help button */ -/******************************************************************************/ - -a.help{ - display: block; - margin: 0px 5px 0px 8px; - height: 17px; - width: 17px; - background: url('help.png') 0% 0% no-repeat; -} +/* rql bar */ -a.help:hover { - background-position: 0px -16px; - text-decoration: none; -} - -/******************************************************************************/ -/* rql bar */ -/******************************************************************************/ - -div#rqlform { - width: 100%; +div#rqlinput { + border: 1px solid #cfceb7; + margin-bottom: 8px; + padding: 3px; + background: #cfceb7; } input#rql{ - width: 80%; - margin-left: 12px; -} - -/******************************************************************************/ -/* user actions menu */ -/******************************************************************************/ - -a.logout, a.logout:visited, a.logout:hover{ - color: #fff; - text-decoration: none; -} - -div#userActionsBox { - width: 14em; - text-align: right; -} - -div#userActionsBox a.popupMenu { - color: black; - text-decoration: underline; -} - -/******************************************************************************/ -/* buttons */ -/******************************************************************************/ - -input#rqlboxsubmit, input#rqlboxsubmit2 { - background: #fffff8 url("go.png") 50% 50% no-repeat; - width: 20px; - height: 20px; - margin: 0px; -} - -input.button, -input.formButton, -input.validateButton, -input.searchButton, -input.loginButton { - border-top: 1px solid #edecd2; - border-left: 1px solid #edecd2; - border-right: 1px solid #cfceb7; - border-bottom: 1px solid #cfceb7; - background: #fffff8 url("button.png") bottom left repeat-x; -} - -input.searchButton { - background: #f0eff0 url("gradient-grey-up.png") left top repeat-x; -} - -input.button, -input.validateButton { - margin: 1em 1em 0px 0px ! important; + width: 95%; } -/******************************************************************************/ -/* primary view */ -/******************************************************************************/ - -.mainInfo { - margin-right: 1em; - padding: 0.2em; -} - -div.mainRelated { - border: none; - margin-right: 1em; - padding: 0.5em 0.2em 0.2em; -} - -div.sideRelated h4, -div.sideRelated h5 { - margin-top: 0px; - margin-bottom: 0px; -} - -div.sideRelated { - margin-right: 1em; - padding: 12px 0px 12px 12px; - min-width: 21em; - max-width: 50em; -} - -div.metadata { - font-size: 90%; - margin: 5px 0px 3px; - color: #666; - font-style: italic; - text-align: right; -} - -div.section { - margin-top: 0.5em; - width:100%; -} - -div.section a:hover { - text-decoration: none; -} - - - -/******************************************************************************/ -/* boxes */ -/******************************************************************************/ - +/* boxes */ div.navboxes { - padding-left: 3px; - margin-top: 8px; + margin-top: 8px; } div.boxFrame { @@ -594,12 +404,14 @@ color: #111100; } + a.boxMenu { background: transparent url("puce_down.png") 98% 6px no-repeat; display: block; padding: 1px 9px 1px 3px; } + a.popupMenu { background: transparent url("puce_down_black.png") 2% 6px no-repeat; padding-left: 2em; @@ -648,6 +460,12 @@ background: #eeedd9; } +ul.sideBox li{ + list-style: none; + background: none; + padding: 0px 0px 1px 1px; + } + div.sideBoxBody { padding: 0.2em 5px; } @@ -680,62 +498,45 @@ padding:0px 5px; } -/******************************************************************************/ -/* inline edition and generic form classes */ -/******************************************************************************/ +/* FIXME rqlboxsubmit2 still necessary ? */ -div.inlineedit { - display: none; +input.rqlsubmit, +input#rqlboxsubmit2 { + background: #fffff8 url("go.png") 50% 50% no-repeat; + width: 20px; + height: 20px; + margin: 0px; } -div.editableField { - display: inline; -} - -div.editableField:hover, -div.editableField p:hover { - background-color: #eeedd9; +input#norql{ + width:13em; + margin-right: 2px; } -option.separator { - font-weight: bold; - background: #ccc; - text-align: center; -} - -input.error { - background: transparent url("error.png") 100% 50% no-repeat; +/* user actions menu */ +a.logout, a.logout:visited, a.logout:hover{ + color: #fff; + text-decoration: none; } -span.error { - display: block; - font-weight: bold; - color: #ed0d0d; +div#userActionsBox { + width: 14em; + text-align: right; } -/******************************************************************************/ -/* navigation */ -/******************************************************************************/ +div#userActionsBox a.popupMenu { + color: black; + text-decoration: underline; +} +/**************/ +/* navigation */ +/**************/ div#etyperestriction { margin-bottom: 1ex; border-bottom: 1px solid #ccc; } -div.navigation a { - text-align: center; - text-decoration: none; -} - -div.prevnext { - width: 100%; - margin-bottom: 1em; -} - -div.prevnext a { - color: #000; -} - span.slice a:visited, span.slice a:hover{ color: #555544; @@ -746,15 +547,95 @@ color: #000; } -/******************************************************************************/ -/* messages */ -/******************************************************************************/ +/* FIXME should be moved to cubes/folder */ +div.navigation a { + text-align: center; + text-decoration: none; +} + +/* FIXME seems to be not used in cubicweb framework */ +div.prevnext { + width: 100%; + margin-bottom: 1em; +} + +div.prevnext a { + color: #000; +} + + +/***************************************/ +/* entity views */ +/***************************************/ + +.mainInfo { + margin-right: 1em; + padding: 0.2em; +} + + +div.mainRelated { + border: none; + margin-right: 1em; + padding: 0.5em 0.2em 0.2em; +} + +div.sideRelated h4, +div.sideRelated h5 { + margin-top: 0px; + margin-bottom: 0px; +} + +div.sideRelated { + margin-right: 1em; + float: right; + padding: 12px 0px 12px 12px; + min-width: 21em; + max-width: 50em; +} + +div.metadata { + font-size: 90%; + margin: 5px 0px 3px; + color: #666; + font-style: italic; + text-align: right; +} + +div.section { + margin-top: 0.5em; + width:100%; +} + +div.section a:hover { + text-decoration: none; +} + +/* basic entity view */ + +div.row { + clear: both; + padding-bottom:0.4px +} + +div.row span.label{ + padding-right:1em +} + +div.field { + margin-left: 0.2em; + display: inline; +} + + +/***************************************/ +/* messages */ +/***************************************/ .warning, .message, -.errorMessage, -.searchMessage, -.statemessage { +.errorMessage , +.searchMessage{ padding: 0.3em 0.3em 0.3em 1em; font-weight: bold; } @@ -772,13 +653,6 @@ background: #f8f8ee; } -div#rqlinput { - border: 1px solid #cfceb7; - margin-bottom: 8px; - padding: 3px; - background: #cfceb7; -} - .message { margin: 0px; background: #f8f8ee url("information.png") 5px center no-repeat; @@ -823,9 +697,9 @@ display: none; } -/******************************************************************************/ -/* listing table */ -/******************************************************************************/ +/***************************************/ +/* listing table */ +/***************************************/ table.listing { margin: 10px 0em; @@ -872,9 +746,10 @@ top: -1px; } -/******************************************************************************/ -/* drag and drop zone (XXX only used in seo for now) */ -/******************************************************************************/ +/***************************************/ +/* drag and drop zone */ +/* (XXX only used in seo for now) */ +/***************************************/ div.droppable { border: 1px dashed black; @@ -887,19 +762,11 @@ background: #f4f5ed; } -/******************************************************************************/ -/* search box */ -/******************************************************************************/ +/****************************************/ +/* filter box FIXME is not used anymore*/ +/****************************************/ -input#norql{ - width:13em; - margin-right: 2px; -} - -/******************************************************************************/ -/* filter box */ -/******************************************************************************/ - +/* #filter_box input { width: 180px; } @@ -916,38 +783,11 @@ #filter_box option.disabled { background: lightgray; } - -/******************************************************************************/ -/* table filter form */ -/******************************************************************************/ - -table.filter th { - font-weight: bold; - background: #ebe8d9 url("button.png") repeat-x; - padding: 0.3em; - border-bottom: 1px solid #cfceb7; - text-align: left; -} +*/ -table.filter div.facet { - padding: 0.6em 0.2em; - margin: 0em 1em; - border: 1px solid #ccc; -} - -table.filter div.facetTitle { - font-weight: bold; -} - - -div#tableActionsBox { - direction:rtl; - float:right -} - -/******************************************************************************/ -/* error view (views/management.py) */ -/******************************************************************************/ +/***************************************/ +/* error view (views/management.py) */ +/***************************************/ div.pycontext { /* html traceback */ font-family: Verdana, sans-serif; @@ -961,32 +801,10 @@ color: #ff0000; } -/******************************************************************************/ -/* index view (views/startup.py) */ -/******************************************************************************/ -table.startup { - width: 100%; -} - -table.startup td { - padding: 0.1em 0.2em; -} - -table.startup td.addcol { - text-align: right; - width: 0.5em; -} - -table.startup th{ - padding-top: 3px; - padding-bottom: 3px; - text-align: left; -} - -/******************************************************************************/ -/* addcombobox */ -/******************************************************************************/ +/***************************************/ +/* addcombobox */ +/***************************************/ input#newopt{ width:120px ; @@ -1005,3 +823,51 @@ display:block; float:left; } + +/***************************************/ +/* buttons */ +/***************************************/ + +input.button{ + margin: 1em 1em 0px 0px; + border: 1px solid #edecd2; + border-color:#edecd2 #cfceb7 #cfceb7 #edecd2; + background: #fffff8 url("button.png") bottom left repeat-x; +} + +/* FileItemInnerView jquery.treeview.css */ +.folder { + /* disable odd/even under folder class */ + background-color: transparent; +} + +/***************************************/ +/* footer */ +/***************************************/ + +div.footer { + text-align: center; +} +div.footer a { + color: #000; + text-decoration: none; +} + + +/****************************************/ +/* FIXME must by managed by cubes */ +/****************************************/ +.needsvalidation { + font-style: italic; + color: gray; +} + + +/***************************************/ +/* FIXME : Deprecated ? entity view ? */ +/***************************************/ +.title { + text-align: left; + font-size: large; + font-weight: bold; +} diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.facets.css --- a/web/data/cubicweb.facets.css Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.facets.css Wed Jun 03 19:42:23 2009 +0200 @@ -69,7 +69,7 @@ } div#filter_box div.boxTitle { - visibility: none; + visibility: hidden; display: none; } diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.form.css --- a/web/data/cubicweb.form.css Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.form.css Wed Jun 03 19:42:23 2009 +0200 @@ -9,7 +9,7 @@ width: 100%; font-size : 160%; font-weight: bold; - color: orangered; + color: #ff4500; padding-bottom : 0.4em; text-transform: capitalize; background: url("bg_trame_grise.png") left bottom repeat-x; @@ -178,6 +178,35 @@ margin-left: 2em; } +/*FIXME inlineedit not used ?*/ +div.inlineedit { + display: none;} + +div.editableField { + display: inline; +} + +div.editableField:hover, +div.editableField p:hover { + background-color: #eeedd9; +} + +input.error { + background: transparent url("error.png") 100% 50% no-repeat; +} + +span.error { + display: block; + font-weight: bold; + color: #ed0d0d; +} + +option.separator { + font-weight: bold; + background: #ccc; + text-align: center; +} + div.trame_grise { background: url("bg_trame_grise.png") left top repeat-x; } @@ -189,3 +218,21 @@ padding-left: 2em; background : #f8f8ee url("information.png") 5px center no-repeat ; } + +.helper{ + font-size: 96%; + color: #555544; +} + +.helper:hover { + color: #000; + cursor: default; +} + +input.validateButton { + margin: 1em 1em 0px 0px; + border: 1px solid #edecd2; + border-color:#edecd2 #cfceb7 #cfceb7 #edecd2; + background: #fffff8 url("button.png") bottom left repeat-x; +} + diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.formfilter.js --- a/web/data/cubicweb.formfilter.js Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.formfilter.js Wed Jun 03 19:42:23 2009 +0200 @@ -109,10 +109,9 @@ root = root || document; jQuery(root).find('form').each(function () { var form = jQuery(this); - var facetargs = form.attr('cubicweb:facetargs'); - if (facetargs) { + var facetargs = evalJSON(form.attr('cubicweb:facetargs')); + if (facetargs !== undefined && facetargs.length) { form.submit(function() { - var facetargs = evalJSON(form.attr('cubicweb:facetargs')); buildRQL.apply(null, facetargs); //(divid, vid, paginate, extraargs); return false; }); @@ -122,7 +121,6 @@ this.setAttribute('cubicweb:idx', i); }); facet.find('div.facetCheckBox').click(function () { - var facetargs = evalJSON(form.attr('cubicweb:facetargs')); var $this = jQuery(this); if ($this.hasClass('facetValueSelected')) { $this.removeClass('facetValueSelected'); diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.gmap.js --- a/web/data/cubicweb.gmap.js Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.gmap.js Wed Jun 03 19:42:23 2009 +0200 @@ -26,7 +26,7 @@ var self = this; // bind this to a local variable jQuery.getJSON(jsonurl, function(geodata) { if (geodata.center) { - var zoomLevel = 8; // FIXME arbitrary ! + var zoomLevel = geodata.zoomlevel; map.setCenter(new GLatLng(geodata.center.latitude, geodata.center.longitude), zoomLevel); } diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.lazy.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/data/cubicweb.lazy.js Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,14 @@ + +function load_now(eltsel, holesel, reloadable) { + var lazydiv = jQuery(eltsel); + var hole = lazydiv.children(holesel); + if ((hole.length == 0) && !reloadable) { + /* the hole is already filled */ + return; + } + lazydiv.loadxhtml(lazydiv.attr('cubicweb:loadurl')); +} + +function trigger_load(divid) { + jQuery('#lazy-' + divid).trigger('load_' + divid); +} diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.login.css --- a/web/data/cubicweb.login.css Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.login.css Wed Jun 03 19:42:23 2009 +0200 @@ -76,7 +76,8 @@ } input.loginButton { - display:block; + border: 1px solid #edecd2; + border-color:#edecd2 #cfceb7 #cfceb7 #edecd2; margin: 2px 0px 0px; background: #f0eff0 url("gradient-grey-up.png") left top repeat-x; } diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.manageview.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/data/cubicweb.manageview.css Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,22 @@ +/***************************************/ +/* Manage view (views/startup.py) */ +/***************************************/ + +table.startup { + width: 100%; +} + +table.startup td { + padding: 0.1em 0.2em; +} + +table.startup td.addcol { + text-align: right; + width: 0.5em; +} + +table.startup th{ + padding-top: 3px; + padding-bottom: 3px; + text-align: left; +} diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.preferences.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/data/cubicweb.preferences.js Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,10 @@ +/* toggle visibility of an element by its id + * & set current visibility status in a cookie + * XXX whenever used outside of preferences, don't forget to + * move me in a more appropriate place + */ +function toggle_and_remember_visibility(elemId, cookiename) { + jqNode(elemId).toggleClass('hidden'); + async_remote_exec('set_cookie', cookiename, + jQuery('#' + elemId).attr('class')); +} diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.schema.css --- a/web/data/cubicweb.schema.css Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.schema.css Wed Jun 03 19:42:23 2009 +0200 @@ -5,6 +5,16 @@ * :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr */ + +.titleUnderline { + color: #000; + clear: left; + margin: 0px; + padding-top: 0.5em; + border-bottom: 1px solid black; +} + + div.relationDefinition { float: left; position: relative; diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.tableview.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/data/cubicweb.tableview.css Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,27 @@ +/***************************************/ +/* filter table form */ +/***************************************/ + +table.filter th { + font-weight: bold; + background: #ebe8d9 url("button.png") repeat-x; + padding: 0.3em; + border-bottom: 1px solid #cfceb7; + text-align: left; +} + +table.filter div.facet { + padding: 0.6em 0.2em; + margin: 0em 1em; + border: 1px solid #ccc; +} + +table.filter div.facetTitle { + font-weight: bold; +} + + +div#tableActionsBox { + direction:rtl; + float:right +} diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.tabs.js --- a/web/data/cubicweb.tabs.js Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.tabs.js Wed Jun 03 19:42:23 2009 +0200 @@ -1,5 +1,6 @@ -function set_tab(tabname) { - // set appropriate cookie - // XXX see if we can no just do it with jQuery - async_remote_exec('remember_active_tab', tabname); +function set_tab(tabname, cookiename) { + // set appropriate cookie + async_remote_exec('set_cookie', cookiename, tabname); + // trigger show + tabname event + trigger_load(tabname); } diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.timetable.css --- a/web/data/cubicweb.timetable.css Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.timetable.css Wed Jun 03 19:42:23 2009 +0200 @@ -12,7 +12,7 @@ table.timetable th { padding:1pt; - align:center; + text-align:center; } @@ -52,7 +52,7 @@ font-family: Verdana, sans-serif; padding-left: 4pt; padding-right: 4pt; - align: left; + text-align: left; width: auto; } diff -r a81d3babb582 -r 85eae6a79f75 web/data/cubicweb.widgets.js --- a/web/data/cubicweb.widgets.js Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/cubicweb.widgets.js Wed Jun 03 19:42:23 2009 +0200 @@ -181,14 +181,6 @@ } } -Widgets.TreeView = defclass("TreeView", null, { - __init__: function(wdgnode) { - jQuery(wdgnode).treeview({toggle: toggleTree, - prerendered: true - }); - } -}); - /* widget based on SIMILE's timeline widget * http://code.google.com/p/simile-widgets/ diff -r a81d3babb582 -r 85eae6a79f75 web/data/jquery.corner.js --- a/web/data/jquery.corner.js Thu Jan 15 10:13:25 2009 +0100 +++ b/web/data/jquery.corner.js Wed Jun 03 19:42:23 2009 +0200 @@ -1,1 +1,178 @@ -if(!document.createElement('canvas').getContext){(function(){var m=Math;var y=m.round;var z=m.sin;var A=m.cos;var Z=10;var B=Z/2;function getContext(){if(this.context_){return this.context_}return this.context_=new CanvasRenderingContext2D_(this)}var C=Array.prototype.slice;function bind(f,b,c){var a=C.call(arguments,2);return function(){return f.apply(b,a.concat(C.call(arguments)))}}var D={init:function(a){if(/MSIE/.test(navigator.userAgent)&&!window.opera){var b=a||document;b.createElement('canvas');b.attachEvent('onreadystatechange',bind(this.init_,this,b))}},init_:function(a){if(!a.namespaces['g_vml_']){a.namespaces.add('g_vml_','urn:schemas-microsoft-com:vml')}if(!a.styleSheets['ex_canvas_']){var b=a.createStyleSheet();b.owningElement.id='ex_canvas_';b.cssText='canvas{display:inline-block;overflow:hidden;'+'text-align:left;width:300px;height:150px}'+'g_vml_\\:*{behavior:url(#default#VML)}'}},i:function(a){if(!a.getContext){a.getContext=getContext;a.attachEvent('onpropertychange',onPropertyChange);a.attachEvent('onresize',onResize);var b=a.attributes;if(b.width&&b.width.specified){a.style.width=b.width.nodeValue+'px'}else{a.width=a.clientWidth}if(b.height&&b.height.specified){a.style.height=b.height.nodeValue+'px'}else{a.height=a.clientHeight}}return a}};function onPropertyChange(e){var a=e.srcElement;switch(e.propertyName){case'width':a.style.width=a.attributes.width.nodeValue+'px';a.getContext().clearRect();break;case'height':a.style.height=a.attributes.height.nodeValue+'px';a.getContext().clearRect();break}}function onResize(e){var a=e.srcElement;if(a.firstChild){a.firstChild.style.width=a.clientWidth+'px';a.firstChild.style.height=a.clientHeight+'px'}}D.init();var E=[];for(var i=0;i<16;i++){for(var j=0;j<16;j++){E[i*16+j]=i.toString(16)+j.toString(16)}}function createMatrixIdentity(){return[[1,0,0],[0,1,0],[0,0,1]]}function processStyle(a){var b,alpha=1;a=String(a);if(a.substring(0,3)=='rgb'){var c=a.indexOf('(',3);var d=a.indexOf(')',c+1);var e=a.substring(c+1,d).split(',');b='#';for(var i=0;i<3;i++){b+=E[Number(e[i])]}if(e.length==4&&a.substr(3,1)=='a'){alpha=e[3]}}else{b=a}return[b,alpha]}function processLineCap(a){switch(a){case'butt':return'flat';case'round':return'round';case'square':default:return'square'}}function CanvasRenderingContext2D_(a){this.m_=createMatrixIdentity();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle='#000';this.fillStyle='#000';this.lineWidth=1;this.lineJoin='miter';this.lineCap='butt';this.miterLimit=Z*1;this.globalAlpha=1;this.canvas=a;var b=a.ownerDocument.createElement('div');b.style.width=a.clientWidth+'px';b.style.height=a.clientHeight+'px';b.style.overflow='hidden';b.style.position='absolute';a.appendChild(b);this.element_=b;this.arcScaleX_=1;this.arcScaleY_=1}var F=CanvasRenderingContext2D_.prototype;F.clearRect=function(){this.element_.innerHTML='';this.currentPath_=[]};F.beginPath=function(){this.currentPath_=[]};F.moveTo=function(a,b){var p=this.getCoords_(a,b);this.currentPath_.push({type:'moveTo',x:p.x,y:p.y});this.currentX_=p.x;this.currentY_=p.y};F.lineTo=function(a,b){var p=this.getCoords_(a,b);this.currentPath_.push({type:'lineTo',x:p.x,y:p.y});this.currentX_=p.x;this.currentY_=p.y};F.bezierCurveTo=function(a,b,c,d,e,f){var p=this.getCoords_(e,f);var g=this.getCoords_(a,b);var h=this.getCoords_(c,d);this.currentPath_.push({type:'bezierCurveTo',cp1x:g.x,cp1y:g.y,cp2x:h.x,cp2y:h.y,x:p.x,y:p.y});this.currentX_=p.x;this.currentY_=p.y};F.fillRect=function(a,b,c,d){this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath();this.fill();this.currentPath_=[]};F.createLinearGradient=function(a,b,c,d){return new CanvasGradient_('gradient')};F.createRadialGradient=function(a,b,c,d,e,f){var g=new CanvasGradient_('gradientradial');g.radius1_=c;g.radius2_=f;g.focus_.x=a;g.focus_.y=b;return g};F.stroke=function(d){var e=[];var f=false;var a=processStyle(d?this.fillStyle:this.strokeStyle);var g=a[0];var h=a[1]*this.globalAlpha;var W=10;var H=10;e.push('');if(typeof this.fillStyle=='object'){var m={x:'50%',y:'50%'};var n=l.x-k.x;var o=l.y-k.y;var q=n>o?n:o;m.x=y(this.fillStyle.focus_.x/n*100+50)+'%';m.y=y(this.fillStyle.focus_.y/o*100+50)+'%';var r=[];if(this.fillStyle.type_=='gradientradial'){var s=this.fillStyle.radius1_/q*100;var t=this.fillStyle.radius2_/q*100-s}else{var s=0;var t=100}var u={offset:null,color:null};var v={offset:null,color:null};this.fillStyle.colors_.sort(function(a,b){return a.offset-b.offset});for(var i=0;iu.offset||u.offset==null){u.offset=w.offset;u.color=w.color}if(w.offset')}else if(d){e.push('')}else{var x=Math.max(this.arcScaleX_,this.arcScaleY_)*this.lineWidth;e.push('')}e.push('');this.element_.insertAdjacentHTML('beforeEnd',e.join(''))};F.fill=function(){this.stroke(true)};F.closePath=function(){this.currentPath_.push({type:'close'})};F.getCoords_=function(a,b){return{x:Z*(a*this.m_[0][0]+b*this.m_[1][0]+this.m_[2][0])-B,y:Z*(a*this.m_[0][1]+b*this.m_[1][1]+this.m_[2][1])-B}};function CanvasPattern_(){}G_vmlCMjrc=D})()}if(jQuery.browser.msie){document.execCommand("BackgroundImageCache",false,true)}(function($){var N=$.browser.msie;var O=N&&!window.XMLHttpRequest;var P=$.browser.opera;var Q=typeof document.createElement('canvas').getContext=="function";var R=function(i){return parseInt(i,10)||0};var S=function(a,b,c){var x=a,y;if(x.currentStyle){y=x.currentStyle[b]}else if(window.getComputedStyle){if(typeof arguments[2]=="string")b=c;y=document.defaultView.getComputedStyle(x,null).getPropertyValue(b)}return y};var T=function(a,p){return S(a,'border'+p+'Color','border-'+p.toLowerCase()+'-color')};var U=function(a,p){if(a.currentStyle&&!P){w=a.currentStyle['border'+p+'Width'];if(w=='thin')w=2;if(w=='medium'&&!(a.currentStyle['border'+p+'Style']=='none'))w=4;if(w=='thick')w=6}else{p=p.toLowerCase();w=document.defaultView.getComputedStyle(a,null).getPropertyValue('border-'+p+'-width')}return R(w)};var V=function(a,i){return a.tagName.toLowerCase()==i};var W=function(e,a,b,c,d){if(e=='tl')return a;if(e=='tr')return b;if(e=='bl')return c;if(e=='br')return d};var X=function(a,b,c,d,e,f,g){var h,curve_to;if(d.indexOf('rgba')!=-1){var i=/^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/;var j=i.exec(d);if(j){var k=[R(j[1]),R(j[2]),R(j[3])];d='rgb('+k[0]+', '+k[1]+', '+k[2]+')'}}var l=a.getContext('2d');if(b==1||g=='notch'){if(e>0&&b>1){l.fillStyle=f;l.fillRect(0,0,b,b);l.fillStyle=d;h=W(c,[0-e,0-e],[e,0-e],[0-e,e],[e,e]);l.fillRect(h[0],h[1],b,b)}else{l.fillStyle=d;l.fillRect(0,0,b,b)}return a}else if(g=='bevel'){h=W(c,[0,0,0,b,b,0,0,0],[0,0,b,b,b,0,0,0],[0,0,b,b,0,b,0,0],[b,b,b,0,0,b,b,b]);l.fillStyle=d;l.beginPath();l.moveTo(h[0],h[1]);l.lineTo(h[2],h[3]);l.lineTo(h[4],h[5]);l.lineTo(h[6],h[7]);l.fill();if(e>0&&e0&&e'+p.html()+'');p.css('zoom','1');if(O){p.children(".JrcTdContainer").get(0).style.setExpression("height","this.parentNode.offsetHeight")}}p.children(".JrcTdContainer").append(a)}else{p.append(a)}};if(N){var ba=document.createStyleSheet();ba.media='print';ba.cssText='.jrcIECanvasDiv { display:none !important; }'}var bb=function(D){if(this.length==0||!(Q||N)){return this}if(D=="destroy"){return this.each(function(){var p,elm=$(this);if(elm.is(".jrcRounded")){if(typeof elm.data("ie6tmr.jrc")=='number')window.clearInterval(elm.data("ie6tmr.jrc"));if(elm.is("table"))p=elm.children("tbody").children("tr:first").children("td:first");else if(elm.is("td"))p=elm.children(".JrcTdContainer");else p=elm;p.children(".jrCorner").remove();elm.unbind('mouseleave.jrc').unbind('mouseenter.jrc').removeClass('jrcRounded').removeData('ie6tmr.jrc');if(elm.is("td"))elm.html(elm.children(".JrcTdContainer").html())}})}var o=(D||"").toLowerCase();var E=R((o.match(/(\d+)px/)||[])[1])||"auto";var F=((o.match(/(#[0-9a-f]+)/)||[])[1])||"auto";var G=/round|bevel|notch/;var H=((o.match(G)||['round'])[0]);var I=/hover/.test(o);var J=/oversized/.test(o);var K=o.match("hiddenparent");if(N){var G=/ie6nofix|ie6fixinit|ie6fixexpr|ie6fixonload|ie6fixwidthint|ie6fixheightint|ie6fixbothint/;var L=((o.match(G)||['ie6fixinit'])[0])}var M={tl:/top|left|tl/.test(o),tr:/top|right|tr/.test(o),bl:/bottom|left|bl/.test(o),br:/bottom|right|br/.test(o)};if(!M.tl&&!M.tr&&!M.bl&&!M.br)M={tl:1,tr:1,bl:1,br:1};this.each(function(){var d=$(this),rbg=null,bg,s,b,pr;var a=this;var e=S(this,'display');var f=S(this,'position');var g=S(this,'lineHeight','line-height');if(F=="auto"){s=d.siblings(".jrcRounded:eq(0)");if(s.length>0){b=s.data("rbg.jrc");if(typeof b=="string"){rbg=b}}}if(K||rbg===null){var h=this.parentNode,hidden_parents=new Array(),a=0;while((typeof h=='object')&&!V(h,'html')){if(K&&S(h,'display')=='none'){hidden_parents.push({originalvisibility:S(h,'visibility'),elm:h});h.style.display='block';h.style.visibility='hidden'}var j=S(h,'backgroundColor','background-color');if(rbg===null&&j!="transparent"&&j!="rgba(0, 0, 0, 0)"){rbg=j}h=h.parentNode}if(rbg===null)rbg="#ffffff"}if(F=="auto"){bg=rbg;d.data("rbg.jrc",rbg)}else{bg=F}if(e=='none'){var k=S(this,'visibility');this.style.display='block';this.style.visibility='hidden';var l=true}else{var m=false}var n=d.height();var p=d.width();if(I){var q=o.replace(/hover|ie6nofix|ie6fixinit|ie6fixexpr|ie6fixonload|ie6fixwidthint|ie6fixheightint|ie6fixbothint/g,"");if(L!='ie6nofix')q="ie6fixinit "+q;d.bind("mouseenter.jrc",function(){d.addClass('jrcHover');d.corner(q)});d.bind("mouseleave.jrc",function(){d.removeClass('jrcHover');d.corner(q)})}if(O&&L!='ie6nofix'){this.style.zoom=1;if(L!='ie6fixexpr'){if(d.width()%2!=0)d.width(d.width()+1);if(d.height()%2!=0)d.height(d.height()+1)}$(window).load(function(){if(L=='ie6fixonload'){if(d.css('height')=='auto')d.height(d.css('height'));if(d.width()%2!=0)d.width(d.width()+1);if(d.height()%2!=0)d.height(d.height()+1)}else if(L=='ie6fixwidthint'||L=='ie6fixheightint'||L=='ie6fixbothint'){var c,ie6FixFunction;if(L=='ie6fixheightint'){ie6FixFunction=function(){d.height('auto');var a=d.height();if(a%2!=0)a=a+1;d.css({height:a})}}else if(L=='ie6fixwidthint'){ie6FixFunction=function(){d.width('auto');var a=d.width();if(a%2!=0)a=a+1;d.css({width:a});d.data('lastWidth.jrc',d.get(0).offsetWidth)}}else if(L=='ie6fixbothint'){ie6FixFunction=function(){d.width('auto');d.height('auto');var a=d.width();var b=d.height();if(b%2!=0)b=b+1;if(a%2!=0)a=a+1;d.css({width:a,height:b})}}c=window.setInterval(ie6FixFunction,100);d.data("ie6tmr.jrc",c)}})}var r=n10)E=r/4}if(E>r/2&&!J){E=r/2}E=Math.floor(E);var t=U(this,'Top');var u=U(this,'Right');var v=U(this,'Bottom');var w=U(this,'Left');if(f=='static'&&!V(this,'td')){this.style.position='relative'}else if(f=='fixed'&&N&&!(document.compatMode=='CSS1Compat'&&!O)){this.style.position='absolute'}if(t+u+v+w>0){this.style.overflow='visible'}if(l)d.css({display:'none',visibility:k});if(typeof hidden_parents!="undefined"){for(var i=0;i0);if(y){if(V(this,'table'))pr=d.children("tbody").children("tr:first").children("td:first");else if(V(this,'td'))pr=d.children(".JrcTdContainer");else pr=d}if(M.tl){bordersWidth=t= 0 ) { + if ($.browser.safari && v == 'rgba(0, 0, 0, 0)') + continue; + var rgb = v.match(/\d+/g); + return '#'+ hex2(rgb[0]) + hex2(rgb[1]) + hex2(rgb[2]); + } + if ( v && v != 'transparent' ) + return v; + } + return '#ffffff'; + }; + function getW(i) { + switch(fx) { + case 'round': return Math.round(width*(1-Math.cos(Math.asin(i/width)))); + case 'cool': return Math.round(width*(1+Math.cos(Math.asin(i/width)))); + case 'sharp': return Math.round(width*(1-Math.cos(Math.acos(i/width)))); + case 'bite': return Math.round(width*(Math.cos(Math.asin((width-i-1)/width)))); + case 'slide': return Math.round(width*(Math.atan2(i,width/i))); + case 'jut': return Math.round(width*(Math.atan2(width,(width-i-1)))); + case 'curl': return Math.round(width*(Math.atan(i))); + case 'tear': return Math.round(width*(Math.cos(i))); + case 'wicked': return Math.round(width*(Math.tan(i))); + case 'long': return Math.round(width*(Math.sqrt(i))); + case 'sculpt': return Math.round(width*(Math.log((width-i-1),width))); + case 'dog': return (i&1) ? (i+1) : width; + case 'dog2': return (i&2) ? (i+1) : width; + case 'dog3': return (i&3) ? (i+1) : width; + case 'fray': return (i%2)*width; + case 'notch': return width; + case 'bevel': return i+1; + } + }; + o = (o||"").toLowerCase(); + var keep = /keep/.test(o); // keep borders? + var cc = ((o.match(/cc:(#[0-9a-f]+)/)||[])[1]); // corner color + var sc = ((o.match(/sc:(#[0-9a-f]+)/)||[])[1]); // strip color + var width = parseInt((o.match(/(\d+)px/)||[])[1]) || 10; // corner width + var re = /round|bevel|notch|bite|cool|sharp|slide|jut|curl|tear|fray|wicked|sculpt|long|dog3|dog2|dog/; + var fx = ((o.match(re)||['round'])[0]); + var edges = { T:0, B:1 }; + var opts = { + TL: /top|tl/.test(o), TR: /top|tr/.test(o), + BL: /bottom|bl/.test(o), BR: /bottom|br/.test(o) + }; + if ( !opts.TL && !opts.TR && !opts.BL && !opts.BR ) + opts = { TL:1, TR:1, BL:1, BR:1 }; + var strip = document.createElement('div'); + strip.style.overflow = 'hidden'; + strip.style.height = '1px'; + strip.style.backgroundColor = sc || 'transparent'; + strip.style.borderStyle = 'solid'; + return this.each(function(index){ + var pad = { + T: parseInt($.css(this,'paddingTop'))||0, R: parseInt($.css(this,'paddingRight'))||0, + B: parseInt($.css(this,'paddingBottom'))||0, L: parseInt($.css(this,'paddingLeft'))||0 + }; + + if ($.browser.msie) this.style.zoom = 1; // force 'hasLayout' in IE + if (!keep) this.style.border = 'none'; + strip.style.borderColor = cc || gpc(this.parentNode); + var cssHeight = $.curCSS(this, 'height'); + + for (var j in edges) { + var bot = edges[j]; + // only add stips if needed + if ((bot && (opts.BL || opts.BR)) || (!bot && (opts.TL || opts.TR))) { + strip.style.borderStyle = 'none '+(opts[j+'R']?'solid':'none')+' none '+(opts[j+'L']?'solid':'none'); + var d = document.createElement('div'); + $(d).addClass('jquery-corner'); + var ds = d.style; + + bot ? this.appendChild(d) : this.insertBefore(d, this.firstChild); + + if (bot && cssHeight != 'auto') { + if ($.css(this,'position') == 'static') + this.style.position = 'relative'; + ds.position = 'absolute'; + ds.bottom = ds.left = ds.padding = ds.margin = '0'; + if ($.browser.msie) + ds.setExpression('width', 'this.parentNode.offsetWidth'); + else + ds.width = '100%'; + } + else if (!bot && $.browser.msie) { + if ($.css(this,'position') == 'static') + this.style.position = 'relative'; + ds.position = 'absolute'; + ds.top = ds.left = ds.right = ds.padding = ds.margin = '0'; + + // fix ie6 problem when blocked element has a border width + var bw = 0; + if (ie6 || !$.boxModel) + bw = sz(this,'borderLeftWidth') + sz(this,'borderRightWidth'); + ie6 ? ds.setExpression('width', 'this.parentNode.offsetWidth - '+bw+'+ "px"') : ds.width = '100%'; + } + else { + ds.margin = !bot ? '-'+pad.T+'px -'+pad.R+'px '+(pad.T-width)+'px -'+pad.L+'px' : + (pad.B-width)+'px -'+pad.R+'px -'+pad.B+'px -'+pad.L+'px'; + } + + for (var i=0; i < width; i++) { + var w = Math.max(0,getW(i)); + var e = strip.cloneNode(false); + e.style.borderWidth = '0 '+(opts[j+'R']?w:0)+'px 0 '+(opts[j+'L']?w:0)+'px'; + bot ? d.appendChild(e) : d.insertBefore(e, d.firstChild); + } + } + } + }); +}; + +$.fn.uncorner = function(o) { return $('.jquery-corner', this).remove(); }; + +})(jQuery); diff -r a81d3babb582 -r 85eae6a79f75 web/facet.py --- a/web/facet.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/facet.py Wed Jun 03 19:42:23 2009 +0200 @@ -29,7 +29,7 @@ def prepare_facets_rqlst(rqlst, args=None): """prepare a syntax tree to generate facet filters - + * remove ORDERBY clause * cleanup selection (remove everything) * undefine unnecessary variables @@ -64,7 +64,7 @@ def get_facet(req, facetid, rqlst, mainvar): return req.vreg.object_by_id('facets', facetid, req, rqlst=rqlst, filtered_variable=mainvar) - + def filter_hiddens(w, **kwargs): for key, val in kwargs.items(): @@ -139,7 +139,7 @@ rqlst.add_group_var(newvar) rqlst.add_selected(newvar) return newvar, rel - + def _remove_relation(rqlst, rel, var): """remove a constraint relation from the syntax tree""" # remove the relation @@ -229,10 +229,10 @@ if ovarname == mainvar.name: continue if not has_path(vargraph, ovarname, mainvar.name): - toremove.add(rqlst.defined_vars[ovarname]) + toremove.add(rqlst.defined_vars[ovarname]) - - + + ## base facet classes ######################################################### class AbstractFacet(AcceptMixIn, AppRsetObject): __registerer__ = priority_registerer @@ -243,16 +243,16 @@ help=_('display the box or not')), _('order'): dict(type='Int', default=99, help=_('display order of the box')), - _('context'): dict(type='String', default=None, + _('context'): dict(type='String', default='', # None <-> both - vocabulary=(_('tablefilter'), _('facetbox'), None), + vocabulary=(_('tablefilter'), _('facetbox'), ''), help=_('context where this box should be displayed')), } visible = True - context = None + context = '' needs_update = False start_unfolded = True - + @classmethod def selected(cls, req, rset=None, rqlst=None, context=None, filtered_variable=None): @@ -280,20 +280,20 @@ def operator(self): # OR between selected values by default return self.req.form.get(self.id + '_andor', 'OR') - + def get_widget(self): """return the widget instance to use to display this facet """ raise NotImplementedError - + def add_rql_restrictions(self): """add restriction for this facet into the rql syntax tree""" raise NotImplementedError - + class VocabularyFacet(AbstractFacet): needs_update = True - + def get_widget(self): """return the widget instance to use to display this facet @@ -311,12 +311,12 @@ else: wdg.append(FacetItem(self.req, label, value, value in selected)) return wdg - + def vocabulary(self): """return vocabulary for this facet, eg a list of 2-uple (label, value) """ raise NotImplementedError - + def possible_values(self): """return a list of possible values (as string since it's used to compare to a form value in javascript) for this facet @@ -325,13 +325,13 @@ def support_and(self): return False - + def rqlexec(self, rql, args=None, cachekey=None): try: return self.req.execute(rql, args, cachekey) except Unauthorized: return [] - + class RelationFacet(VocabularyFacet): __selectors__ = (one_has_relation, match_context_prop) @@ -344,10 +344,10 @@ sortfunc = None # ascendant/descendant sorting sortasc = True - + @property def title(self): - return display_name(self.req, self.rtype, form=self.role) + return display_name(self.req, self.rtype, form=self.role) def vocabulary(self): """return vocabulary for this facet, eg a list of 2-uple (label, value) @@ -367,7 +367,7 @@ finally: rqlst.recover() return self.rset_vocabulary(rset) - + def possible_values(self): """return a list of possible values (as string since it's used to compare to a form value in javascript) for this facet @@ -380,7 +380,7 @@ return [str(x) for x, in self.rqlexec(rqlst.as_string())] finally: rqlst.recover() - + def rset_vocabulary(self, rset): _ = self.req._ return [(_(label), eid) for eid, label in rset] @@ -432,7 +432,9 @@ class AttributeFacet(RelationFacet): # attribute type attrtype = 'String' - + # type of comparison: default is an exact match on the attribute value + comparator = '=' # could be '<', '<=', '>', '>=' + def vocabulary(self): """return vocabulary for this facet, eg a list of 2-uple (label, value) """ @@ -452,14 +454,14 @@ finally: rqlst.recover() return self.rset_vocabulary(rset) - + def rset_vocabulary(self, rset): _ = self.req._ return [(_(value), value) for value, in rset] def support_and(self): return False - + def add_rql_restrictions(self): """add restriction for this facet into the rql syntax tree""" value = self.req.form.get(self.id) @@ -467,16 +469,16 @@ return mainvar = self.filtered_variable self.rqlst.add_constant_restriction(mainvar, self.rtype, value, - self.attrtype) + self.attrtype, self.comparator) - + class FilterRQLBuilder(object): """called by javascript to get a rql string from filter form""" def __init__(self, req): self.req = req - + def build_rql(self):#, tablefilter=False): form = self.req.form facetids = form['facets'].split(',') @@ -490,18 +492,18 @@ toupdate.append(facetid) return select.as_string(), toupdate - + ## html widets ################################################################ class FacetVocabularyWidget(HTMLWidget): - + def __init__(self, facet): self.facet = facet self.items = [] def append(self, item): self.items.append(item) - + def _render(self): title = html_escape(self.facet.title) facetid = html_escape(self.facet.id) @@ -527,7 +529,7 @@ self.w(u'\n') self.w(u'\n') - + class FacetStringWidget(HTMLWidget): def __init__(self, facet): self.facet = facet @@ -558,12 +560,14 @@ if self.selected: cssclass = ' facetValueSelected' imgsrc = self.req.datadir_url + self.selected_img + imgalt = self.req._('selected') else: cssclass = '' - imgsrc = self.req.datadir_url + self.unselected_img + imgsrc = self.req.datadir_url + self.unselected_img + imgalt = self.req._('not selected') self.w(u'
\n' % (cssclass, html_escape(unicode(self.value)))) - self.w(u' ' % imgsrc) + self.w(u'%s ' % (imgsrc, imgalt)) self.w(u'%s' % html_escape(self.label)) self.w(u'
') @@ -571,7 +575,7 @@ class FacetSeparator(HTMLWidget): def __init__(self, label=None): self.label = label or u' ' - + def _render(self): pass diff -r a81d3babb582 -r 85eae6a79f75 web/form.py --- a/web/form.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/form.py Wed Jun 03 19:42:23 2009 +0200 @@ -11,7 +11,7 @@ from logilab.mtconverter import html_escape from cubicweb import typed_eid -from cubicweb.common.selectors import req_form_params_selector +from cubicweb.common.selectors import match_form_params from cubicweb.common.registerers import accepts_registerer from cubicweb.common.view import NOINDEX, NOFOLLOW, View, EntityView, AnyRsetView from cubicweb.web import stdmsgs @@ -239,7 +239,7 @@ should list necessary parameters in the form to be accepted. """ __registerer__ = accepts_registerer - __select__ = classmethod(req_form_params_selector) + __select__ = classmethod(match_form_params) form_params = () diff -r a81d3babb582 -r 85eae6a79f75 web/request.py --- a/web/request.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/request.py Wed Jun 03 19:42:23 2009 +0200 @@ -201,7 +201,7 @@ def update_search_state(self): """update the current search state""" searchstate = self.form.get('__mode') - if not searchstate: + if not searchstate and self.cnx is not None: searchstate = self.get_session_data('search_state', 'normal') self.set_search_state(searchstate) @@ -212,7 +212,8 @@ else: self.search_state = ('linksearch', searchstate.split(':')) assert len(self.search_state[-1]) == 4 - self.set_session_data('search_state', searchstate) + if self.cnx is not None: + self.set_session_data('search_state', searchstate) def update_breadcrumbs(self): """stores the last visisted page in session data""" @@ -640,7 +641,11 @@ def xhtml_browser(self): useragent = self.useragent() - if useragent and ('MSIE' in useragent or 'KHTML' in useragent): + # MSIE does not support xml content-type + # quick fix: Opera supports xhtml and handles namespaces + # properly but it breaks jQuery.attr() + if useragent and ('MSIE' in useragent or 'KHTML' in useragent + or 'Opera' in useragent): return False return True diff -r a81d3babb582 -r 85eae6a79f75 web/test/jstest_python.jst --- a/web/test/jstest_python.jst Thu Jan 15 10:13:25 2009 +0100 +++ b/web/test/jstest_python.jst Wed Jun 03 19:42:23 2009 +0200 @@ -1,11 +1,13 @@ +// run tests with the following command line : +// $ crosscheck jstest_python.jst crosscheck.addTest({ setup: function() { crosscheck.load("testutils.js"); crosscheck.load("../data/jquery.js"); - crosscheck.load("../data/compat.js"); - crosscheck.load("../data/python.js"); + crosscheck.load("../data/cubicweb.compat.js"); + crosscheck.load("../data/cubicweb.python.js"); }, test_basic_number_parsing: function () { diff -r a81d3babb582 -r 85eae6a79f75 web/test/test_views.py --- a/web/test/test_views.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/test/test_views.py Wed Jun 03 19:42:23 2009 +0200 @@ -37,11 +37,11 @@ def test_manual_tests(self): rset = self.execute('Any P,F,S WHERE P is EUser, P firstname F, P surname S') self.view('table', rset, template=None, displayfilter=True, displaycols=[0,2]) - rset = self.execute('Any P,F,S WHERE P is EUser, P firstname F, P surname S LIMIT 1') + rset = self.execute('Any P,F,S LIMIT 1 WHERE P is EUser, P firstname F, P surname S') rset.req.form['rtype'] = 'firstname' - self.view('editrelation', rset, template=None, htmlcheck=False) + self.view('editrelation', rset, template=None) rset.req.form['rtype'] = 'use_email' - self.view('editrelation', rset, template=None, htmlcheck=False) + self.view('editrelation', rset, template=None) def test_sortable_js_added(self): @@ -49,7 +49,7 @@ # sortable.js should not be included by default self.failIf('jquery.tablesorter.js' in self.view('oneline', rset)) # but should be included by the tableview - rset = self.execute('Any P,F,S WHERE P is EUser, P firstname F, P surname S LIMIT 1') + rset = self.execute('Any P,F,S LIMIT 1 WHERE P is EUser, P firstname F, P surname S') self.failUnless('jquery.tablesorter.js' in self.view('table', rset)) def test_js_added_only_once(self): diff -r a81d3babb582 -r 85eae6a79f75 web/test/unittest_owl.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/test/unittest_owl.py Wed Jun 03 19:42:23 2009 +0200 @@ -0,0 +1,4075 @@ +"""unittests for schema2dot""" + +import os + +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.compat import set +from cubicweb.devtools.testlib import WebTest + +from lxml import etree +from StringIO import StringIO + + +class OWLTC(WebTest): + + def test_schema2owl(self): + + parser = etree.XMLParser(dtd_validation=True) + + owl= StringIO(''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +''') + + rdf = StringIO(''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ''') + + + xmlschema_rdf = etree.parse(rdf) + xmlschema_owl = etree.parse(owl) + + owlschema = etree.XMLSchema(xmlschema_owl) + valid = StringIO(''' + + + + + + + ]> + + + + + inst_jplorg2 Cubicweb OWL Ontology + + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + + n + + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + 1 + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + 1 + 1 + + + + + + 1 + 1 + + + + + + n + + + + + + n + + + + + + 1 + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + 1 + + + + + + 1 + + + + + + 1 + + + + + + 1 + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + n + + + + + + 1 + 1 + + + + + + n + + + + + + 1 + 1 + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + n + + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ''') + doc = etree.parse(valid) + owlschema.validate(doc) + +if __name__ == '__main__': + unittest_main() + diff -r a81d3babb582 -r 85eae6a79f75 web/test/unittest_viewselector.py --- a/web/test/unittest_viewselector.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/test/unittest_viewselector.py Wed Jun 03 19:42:23 2009 +0200 @@ -1,6 +1,5 @@ # -*- coding: iso-8859-1 -*- """XXX rename, split, reorganize this - """ import os.path as osp @@ -10,16 +9,17 @@ from cubicweb import CW_SOFTWARE_ROOT as BASE, Binary -from cubicweb.common.selectors import in_group_selector +from cubicweb.common.selectors import match_user_group from cubicweb.web._exceptions import NoSelectableObject from cubicweb.web.action import Action from cubicweb.web.views import (baseviews, tableview, baseforms, calendar, - management, embedding, actions, startup, - euser, schemaentities, xbel, vcard, - idownloadable, wdoc, debug) + management, embedding, actions, startup, + euser, schemaentities, xbel, vcard, + treeview, idownloadable, wdoc, debug) from cubicweb.entities.lib import Card from cubicweb.interfaces import IMileStone +from cubicweb.web.views import owl USERACTIONS = [('myprefs', actions.UserPreferencesAction), ('myinfos', actions.UserInfoAction), @@ -75,6 +75,7 @@ ('index', startup.IndexView), ('info', management.ProcessInformationView), ('manage', startup.ManageView), + ('owl', owl.OWLView), ('schema', startup.SchemaView), ('systemepropertiesform', management.SystemEpropertiesForm)]) # no entity but etype @@ -94,14 +95,18 @@ [('csvexport', baseviews.CSVRsetView), ('ecsvexport', baseviews.CSVEntityView), ('editable-table', tableview.EditableTableView), + ('filetree', treeview.FileTreeView), ('list', baseviews.ListView), ('oneline', baseviews.OneLineView), + ('owlabox', owl.OWLABOXView), ('primary', baseviews.PrimaryView), + ('rsetxml', baseviews.XMLRsetView), ('rss', baseviews.RssView), ('secondary', baseviews.SecondaryView), ('security', management.SecurityManagementView), ('table', tableview.TableView), ('text', baseviews.TextView), + ('treeview', treeview.TreeView), ('xbel', xbel.XbelView), ('xml', baseviews.XmlView), ]) @@ -111,14 +116,18 @@ [('csvexport', baseviews.CSVRsetView), ('ecsvexport', baseviews.CSVEntityView), ('editable-table', tableview.EditableTableView), + ('filetree', treeview.FileTreeView), ('list', baseviews.ListView), ('oneline', baseviews.OneLineView), + ('owlabox', owl.OWLABOXView), ('primary', baseviews.PrimaryView), + ('rsetxml', baseviews.XMLRsetView), ('rss', baseviews.RssView), ('secondary', baseviews.SecondaryView), ('security', management.SecurityManagementView), ('table', tableview.TableView), ('text', baseviews.TextView), + ('treeview', treeview.TreeView), ('xbel', xbel.XbelView), ('xml', baseviews.XmlView), ]) @@ -128,14 +137,18 @@ [('csvexport', baseviews.CSVRsetView), ('ecsvexport', baseviews.CSVEntityView), ('editable-table', tableview.EditableTableView), + ('filetree', treeview.FileTreeView), ('list', baseviews.ListView), ('oneline', baseviews.OneLineView), + ('owlabox', owl.OWLABOXView), ('primary', baseviews.PrimaryView), + ('rsetxml', baseviews.XMLRsetView), ('rss', baseviews.RssView), ('secondary', baseviews.SecondaryView), ('security', management.SecurityManagementView), ('table', tableview.TableView), ('text', baseviews.TextView), + ('treeview', treeview.TreeView), ('xbel', xbel.XbelView), ('xml', baseviews.XmlView), ]) @@ -144,6 +157,7 @@ self.assertListEqual(self.pviews(req, rset), [('csvexport', baseviews.CSVRsetView), ('editable-table', tableview.EditableTableView), + ('rsetxml', baseviews.XMLRsetView), ('table', tableview.TableView), ]) # list of euser entities @@ -152,14 +166,19 @@ [('csvexport', baseviews.CSVRsetView), ('ecsvexport', baseviews.CSVEntityView), ('editable-table', tableview.EditableTableView), + ('filetree', treeview.FileTreeView), + ('foaf', euser.FoafView), ('list', baseviews.ListView), ('oneline', baseviews.OneLineView), + ('owlabox', owl.OWLABOXView), ('primary', euser.EUserPrimaryView), + ('rsetxml', baseviews.XMLRsetView), ('rss', baseviews.RssView), ('secondary', baseviews.SecondaryView), ('security', management.SecurityManagementView), ('table', tableview.TableView), ('text', baseviews.TextView), + ('treeview', treeview.TreeView), ('vcard', vcard.VCardEUserView), ('xbel', xbel.XbelView), ('xml', baseviews.XmlView), @@ -387,12 +406,12 @@ - def test_owners_in_group_selector(self): - """tests usage of 'owners' group with in_group_selector""" + def test_owners_match_user_group(self): + """tests usage of 'owners' group with match_user_group""" class SomeAction(Action): id = 'yo' category = 'foo' - __selectors__ = (in_group_selector,) + __selectors__ = (match_user_group,) require_groups = ('owners', ) self.vreg.register_vobject_class(SomeAction) self.failUnless(SomeAction in self.vreg['actions']['yo'], self.vreg['actions']) diff -r a81d3babb582 -r 85eae6a79f75 web/views/actions.py --- a/web/views/actions.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/actions.py Wed Jun 03 19:42:23 2009 +0200 @@ -6,11 +6,12 @@ """ __docformat__ = "restructuredtext en" -from cubicweb import UnknownEid -from cubicweb.common.selectors import * +from cubicweb.common.selectors import (searchstate_accept, match_user_group, yes, + one_line_rset, two_lines_rset, one_etype_rset, + authenticated_user, + match_search_state, chainfirst, chainall) -from cubicweb.web.action import (Action, EntityAction, LinkToEntityAction, - LinkToEntityAction2) +from cubicweb.web.action import Action, EntityAction, LinkToEntityAction from cubicweb.web.views import linksearch_select_url, linksearch_match from cubicweb.web.views.baseviews import vid_from_rset @@ -55,7 +56,7 @@ class ViewAction(Action): category = 'mainactions' - __selectors__ = (in_group_selector, searchstate_accept) + __selectors__ = (match_user_group, searchstate_accept) require_groups = ('users', 'managers') order = 0 @@ -110,7 +111,7 @@ class MultipleEditAction(EntityAction): category = 'mainactions' - __selectors__ = (two_lines_rset, oneetyperset_selector, + __selectors__ = (two_lines_rset, one_etype_rset, searchstate_accept) schema_action = 'update' order = 10 @@ -198,7 +199,7 @@ return 0 __selectors__ = (match_search_state, chainfirst(etype_rset_selector, - chainall(two_lines_rset, oneetyperset_selector, + chainall(two_lines_rset, one_etype_rset, has_add_perm_selector))) @property @@ -219,7 +220,7 @@ class UserPreferencesAction(Action): category = 'useractions' - __selectors__ = not_anonymous_selector, + __selectors__ = authenticated_user, order = 10 id = 'myprefs' @@ -231,7 +232,7 @@ class UserInfoAction(Action): category = 'useractions' - __selectors__ = not_anonymous_selector, + __selectors__ = authenticated_user, order = 20 id = 'myinfos' @@ -243,7 +244,7 @@ class LogoutAction(Action): category = 'useractions' - __selectors__ = not_anonymous_selector, + __selectors__ = authenticated_user, order = 30 id = 'logout' @@ -258,7 +259,7 @@ class ManagersAction(Action): category = 'siteactions' __abstract__ = True - __selectors__ = in_group_selector, + __selectors__ = match_user_group, require_groups = ('managers',) def url(self): @@ -301,7 +302,7 @@ return self.rset.get_entity(self.row or 0, self.col or 0).actual_url() class UserPreferencesEntityAction(EntityAction): - __selectors__ = EntityAction.__selectors__ + (one_line_rset, in_group_selector,) + __selectors__ = EntityAction.__selectors__ + (one_line_rset, match_user_group,) require_groups = ('owners', 'managers') category = 'mainactions' accepts = ('EUser',) diff -r a81d3babb582 -r 85eae6a79f75 web/views/ajaxedit.py --- a/web/views/ajaxedit.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/ajaxedit.py Wed Jun 03 19:42:23 2009 +0200 @@ -6,8 +6,8 @@ """ __docformat__ = "restructuredtext en" -from cubicweb.common.selectors import (chainfirst, req_form_params_selector, - kwargs_selector) +from cubicweb.common.selectors import (chainfirst, match_form_params, + match_kwargs) from cubicweb.web.box import EditRelationBoxTemplate class AddRelationView(EditRelationBoxTemplate): @@ -18,7 +18,7 @@ class attributes. """ __registry__ = 'views' - __selectors__ = (chainfirst(req_form_params_selector, kwargs_selector),) + __selectors__ = (chainfirst(match_form_params, match_kwargs),) property_defs = {} # don't want to inherit this from Box id = 'xaddrelation' expected_kwargs = form_params = ('rtype', 'target') diff -r a81d3babb582 -r 85eae6a79f75 web/views/basecomponents.py --- a/web/views/basecomponents.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/basecomponents.py Wed Jun 03 19:42:23 2009 +0200 @@ -14,10 +14,10 @@ from cubicweb import Unauthorized from cubicweb.common.uilib import html_escape, toggle_action -from cubicweb.common.selectors import yes, nfentity_selector, one_line_rset +from cubicweb.common.selectors import yes, non_final_entity, one_line_rset from cubicweb.schema import display_name -from cubicweb.common.selectors import (chainfirst, multitype_selector, - req_form_params_selector) +from cubicweb.common.selectors import (chainfirst, two_etypes_rset, + match_form_params) from cubicweb.web.htmlwidgets import MenuWidget, PopupBoxMenu, BoxSeparator, BoxLink from cubicweb.web.component import (VComponent, SingletonVComponent, EntityVComponent, @@ -44,11 +44,11 @@
- +
''' % (not self.propval('visible') and 'hidden' or '', self.build_url('view'), html_escape(rql), req._('full text or RQL query'), req.next_tabindex(), - req._('search'), req.next_tabindex())) + req.next_tabindex())) if self.req.search_state[0] != 'normal': self.w(u'' % ':'.join(req.search_state[1])) @@ -138,9 +138,9 @@ target = 'subject' title = _('Workflow history') - def call(self, view=None): + def cell_call(self, row, col, view=None): _ = self.req._ - eid = self.rset[0][0] + eid = self.rset[row][col] sel = 'Any FS,TS,WF,D' rql = ' ORDERBY D DESC WHERE WF wf_info_for X,'\ 'WF from_state FS, WF to_state TS, WF comment C,'\ @@ -191,7 +191,7 @@ to be able to filter accordingly. """ id = 'etypenavigation' - __select__ = classmethod(chainfirst(multitype_selector, req_form_params_selector)) + __select__ = classmethod(chainfirst(two_etypes_rset, match_form_params)) form_params = ('__restrtype', '__restrtypes', '__restrrql') visible = False # disabled by default @@ -238,14 +238,14 @@ class RSSFeedURL(VComponent): id = 'rss_feed_url' - __selectors__ = (nfentity_selector,) + __selectors__ = (non_final_entity,) def feed_url(self): return self.build_url(rql=self.limited_rql(), vid='rss') class RSSEntityFeedURL(VComponent): id = 'rss_feed_url' - __selectors__ = (nfentity_selector, one_line_rset) + __selectors__ = (non_final_entity, one_line_rset) def feed_url(self): return self.entity(0, 0).rss_feed_url() diff -r a81d3babb582 -r 85eae6a79f75 web/views/basecontrollers.py --- a/web/views/basecontrollers.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/basecontrollers.py Wed Jun 03 19:42:23 2009 +0200 @@ -60,7 +60,6 @@ def publish(self, rset=None): """publish a request, returning an encoded string""" - self.req.update_search_state() template = self.req.property_value('ui.main-template') if template not in self.vreg.registry('templates') : template = self.template @@ -235,7 +234,7 @@ stream.write(u'
') vtitle = self.req.form.get('vtitle') if vtitle: - w(u'

%s

\n' % vtitle) + stream.write(u'

%s

\n' % vtitle) view.pagination(req, rset, view.w, not view.need_navigation) if divid == 'pageContent': stream.write(u'
') @@ -453,7 +452,14 @@ # link the new entity to the main entity rql = 'SET F %(rel)s T WHERE F eid %(eid_to)s, T eid %(eid_from)s' % {'rel' : rel, 'eid_to' : eid_to, 'eid_from' : eid_from} return eid_from - + + def js_set_cookie(self, cookiename, cookievalue): + # XXX we should consider jQuery.Cookie + cookiename, cookievalue = str(cookiename), str(cookievalue) + cookies = self.req.get_cookie() + cookies[cookiename] = cookievalue + self.req.set_cookie(cookies, cookiename) + class SendMailController(Controller): id = 'sendmail' require_groups = ('managers', 'users') diff -r a81d3babb582 -r 85eae6a79f75 web/views/baseforms.py --- a/web/views/baseforms.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/baseforms.py Wed Jun 03 19:42:23 2009 +0200 @@ -2,7 +2,7 @@ or a list of entities of the same type :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -17,9 +17,9 @@ from cubicweb.interfaces import IWorkflowable from cubicweb.common.utils import make_uid from cubicweb.common.uilib import cut -from cubicweb.common.selectors import (etype_form_selector, kwargs_selector, - one_line_rset, interface_selector, - req_form_params_selector, accept_selector) +from cubicweb.common.selectors import (accept_etype, match_kwargs, + one_line_rset, implement_interface, + match_form_params, accept) from cubicweb.common.view import EntityView from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param from cubicweb.web.controller import NAV_FORM_PARAMETERS @@ -33,6 +33,9 @@ title = _('delete') domid = 'deleteconf' onsubmit = None + # don't use navigation, all entities asked to be deleted should be displayed + # else we will only delete the displayed page + need_navigation = False def call(self): """ask for confirmation before real deletion""" @@ -87,7 +90,7 @@ id = 'statuschange' title = _('status change') - __selectors__ = (interface_selector, req_form_params_selector) + __selectors__ = (implement_interface, match_form_params) accepts_interfaces = (IWorkflowable,) form_params = ('treid',) @@ -150,7 +153,7 @@ class ClickAndEditForm(EntityForm): id = 'reledit' - __selectors__ = (kwargs_selector, ) + __selectors__ = (match_kwargs, ) expected_kwargs = ('rtype',) #FIXME editableField class could be toggleable from userprefs @@ -216,7 +219,7 @@ dynamic default values such as the 'tomorrow' date or the user's login being connected """ - __selectors__ = (one_line_rset, accept_selector) + __selectors__ = (one_line_rset, accept) id = 'edition' title = _('edition') @@ -392,17 +395,20 @@ if rschema != 'eid'] def relations_form(self, entity, kwargs): + srels_by_cat = entity.srelations_by_category(('generic', 'metadata'), 'add') + if not srels_by_cat: + return u'' req = self.req _ = self.req._ label = u'%s :' % _('This %s' % entity.e_schema).capitalize() eid = entity.eid html = [] - pendings = list(self.restore_pending_inserts(entity)) w = html.append w(u'
') w(u'%s' % label) w(u'') for row in self.relations_table(entity): + # already linked entities if row[2]: w(u'' % row[0].display_name(req, row[1])) w(u'') w(u'') + pendings = list(self.restore_pending_inserts(entity)) if not pendings: w(u'') else: for row in pendings: + # soon to be linked to entities w(u'' % row[1]) w(u'' % row[3]) w(u'\n') @@ -288,7 +295,7 @@ w(u'
\n') vtitle = self.req.form.get('vtitle') if vtitle: - w(u'

%s

' % (vtitle)) + w(u'

%s

' % html_escape(vtitle)) def topleft_header(self): self.w(u'
%s') @@ -415,10 +421,12 @@ w(u'') w(u'
  
%s') @@ -434,7 +442,8 @@ w(u'') w(u'') @@ -517,7 +526,7 @@ class CreationForm(EditionForm): - __selectors__ = (etype_form_selector, ) + __selectors__ = (accept_etype, ) id = 'creation' title = _('creation') @@ -630,7 +639,7 @@ class InlineEntityCreationForm(InlineFormMixIn, CreationForm): id = 'inline-creation' - __selectors__ = (kwargs_selector, etype_form_selector) + __selectors__ = (match_kwargs, accept_etype) expected_kwargs = ('ptype', 'peid', 'rtype') EDITION_BODY = u'''\ @@ -669,7 +678,7 @@ class InlineEntityEditionForm(InlineFormMixIn, EditionForm): id = 'inline-edition' - __selectors__ = (accept_selector, kwargs_selector) + __selectors__ = (accept, match_kwargs) expected_kwargs = ('ptype', 'peid', 'rtype') EDITION_BODY = u'''\ @@ -872,7 +881,7 @@ class UnrelatedDivs(EntityView): id = 'unrelateddivs' - __selectors__ = (req_form_params_selector,) + __selectors__ = (match_form_params,) form_params = ('relation',) @property diff -r a81d3babb582 -r 85eae6a79f75 web/views/basetemplates.py --- a/web/views/basetemplates.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/basetemplates.py Wed Jun 03 19:42:23 2009 +0200 @@ -2,16 +2,18 @@ """default templates for CubicWeb web client :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" + from logilab.mtconverter import html_escape from cubicweb import NoSelectableObject, ObjectNotFound from cubicweb.common.view import Template, MainTemplate, NOINDEX, NOFOLLOW from cubicweb.common.utils import make_uid +from cubicweb.common.utils import UStringIO from cubicweb.web.views.baseviews import vid_from_rset @@ -56,8 +58,9 @@ title = 'logged out' def content(self, w): + # FIXME Deprecated code ? msg = self.req._('you have been logged out') - w(u'

%s

\n' % msg) + w(u'

%s

\n' % msg) if self.config['anonymous-user']: indexurl = self.build_url('view', vid='index', __message=msg) w(u'

%s

' % ( @@ -162,7 +165,10 @@ self.req, self.rset) if etypefilter and etypefilter.propval('visible'): etypefilter.dispatch(w=self.w) - self.pagination(self.req, self.rset, self.w, not (view and view.need_navigation)) + self.nav_html = UStringIO() + self.pagination(self.req, self.rset, self.nav_html.write, + not (view and view.need_navigation)) + self.w(_(self.nav_html.getvalue())) self.w(u'

\n') def template_html_header(self, content_type, page_title, additional_headers=()): @@ -194,10 +200,11 @@ w(u'
\n') vtitle = self.req.form.get('vtitle') if vtitle: - w(u'

%s

\n' % vtitle) + w(u'

%s

\n' % html_escape(vtitle)) def template_footer(self, view=None): self.w(u'
\n') # close id=contentmain + self.w(_(self.nav_html.getvalue())) self.w(u'
\n') # closes id=pageContent self.content_footer(view) self.w(u'
\n') @@ -418,7 +425,7 @@ req._(ChangeLogView.title).lower())) self.w(u'%s | ' % (req.build_url('doc/about'), req._('about this site'))) - self.w(u'© 2001-2008 Logilab S.A.') + self.w(u'© 2001-2009 Logilab S.A.') self.w(u'') diff -r a81d3babb582 -r 85eae6a79f75 web/views/baseviews.py --- a/web/views/baseviews.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/baseviews.py Wed Jun 03 19:42:23 2009 +0200 @@ -8,24 +8,25 @@ :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" +from warnings import warn from time import timezone from rql import nodes from logilab.common.decorators import cached -from logilab.mtconverter import html_escape, TransformError +from logilab.mtconverter import TransformError, html_escape, xml_escape from cubicweb import Unauthorized, NoSelectableObject, typed_eid -from cubicweb.common.selectors import (yes, nonempty_rset, accept_selector, +from cubicweb.common.selectors import (yes, nonempty_rset, accept, one_line_rset, match_search_state, - req_form_params_selector, accept_rset_selector) + match_form_params, accept_rset) from cubicweb.common.uilib import (cut, printable_value, UnicodeCSVWriter, - ajax_replace_url, rql_for_eid) + ajax_replace_url, rql_for_eid, simple_sgml_tag) from cubicweb.common.view import EntityView, AnyRsetView, EmptyRsetView from cubicweb.web.httpcache import MaxAgeHTTPCacheManager from cubicweb.web.views import vid_from_rset, linksearch_select_url, linksearch_match @@ -56,14 +57,14 @@ """ id = 'final' - def cell_call(self, row, col, props=None, displaytime=False): + def cell_call(self, row, col, props=None, displaytime=False, format='text/html'): etype = self.rset.description[row][col] value = self.rset.rows[row][col] if etype == 'String': entity, rtype = self.rset.related_entity(row, col) if entity is not None: # yes ! - self.w(entity.printable_value(rtype, value)) + self.w(entity.printable_value(rtype, value, format=format)) return if etype in ('Time', 'Interval'): _ = self.req._ @@ -143,12 +144,7 @@ self.w(u'
') self.render_entity_attributes(entity, siderelations) self.w(u'
') - self.w(u'') + self.content_navigation_components('navcontenttop') if self.main_related_section: self.render_entity_relations(entity, siderelations) self.w(u'') @@ -158,13 +154,21 @@ self.w(u'') self.w(u'') self.w(u'') - self.w(u'') - for box in self.vreg.possible_vobjects('boxes', self.req, entity.rset, - col=entity.col, row=entity.row, - view=self, context='incontext'): + for box in self.vreg.possible_vobjects('boxes', self.req, self.rset, + row=self.row, view=self, + context='incontext'): try: - box.dispatch(w=self.w, col=entity.col, row=entity.row) + box.dispatch(w=self.w, row=self.row) except NotImplementedError: # much probably a context insensitive box, which only implements # .call() and not cell_call() @@ -350,10 +354,10 @@ self.w(u'') class TextView(EntityView): - """the simplest text view for an entity - """ + """the simplest text view for an entity""" id = 'text' title = _('text') + content_type = 'text/plain' accepts = 'Any', def call(self, **kwargs): """the view is called for an entire result set, by default loop @@ -571,8 +575,7 @@ self.wview(self.item_vid, self.rset, row=row, col=col) def call(self): - """display a list of entities by calling their view - """ + """display a list of entities by calling their view""" self.w(u'\n' % self.req.encoding) self.w(u'<%s size="%s">\n' % (self.xml_root, len(self.rset))) for i in xrange(self.rset.rowcount): @@ -599,7 +602,7 @@ from base64 import b64encode value = '' % b64encode(value.getvalue()) elif isinstance(value, basestring): - value = value.replace('&', '&').replace('<', '<') + value = xml_escape(value) self.w(u' <%s>%s\n' % (attr, value, attr)) self.w(u'\n' % (entity.e_schema)) @@ -619,21 +622,25 @@ eschema = self.schema.eschema labels = self.columns_labels(False) w(u'\n' % self.req.encoding) - w(u'<%s>\n' % self.xml_root) + w(u'<%s query="%s">\n' % (self.xml_root, html_escape(rset.printable_rql()))) for rowindex, row in enumerate(self.rset): w(u' \n') for colindex, val in enumerate(row): etype = descr[rowindex][colindex] tag = labels[colindex] + attrs = {} + if '(' in tag: + attrs['expr'] = tag + tag = 'funccall' if val is not None and not eschema(etype).is_final(): + attrs['eid'] = val # csvrow.append(val) # val is eid in that case - content = self.view('textincontext', rset, - row=rowindex, col=colindex) - w(u' <%s eid="%s">%s\n' % (tag, val, html_escape(content), tag)) + val = self.view('textincontext', rset, + row=rowindex, col=colindex) else: - content = self.view('final', rset, displaytime=True, - row=rowindex, col=colindex) - w(u' <%s>%s\n' % (tag, html_escape(content), tag)) + val = self.view('final', rset, displaytime=True, + row=rowindex, col=colindex, format='text/plain') + w(simple_sgml_tag(tag, val, **attrs)) w(u' \n') w(u'\n' % self.xml_root) @@ -740,7 +747,8 @@ content = self.view('textincontext', rset, row=rowindex, col=colindex) else: - content = self.view('final', rset, displaytime=True, + content = self.view('final', rset, + displaytime=True, format='text/plain', row=rowindex, col=colindex) csvrow.append(content) writer.writerow(csvrow) @@ -785,7 +793,7 @@ """ id = 'search-associate' title = _('search for association') - __selectors__ = (one_line_rset, match_search_state, accept_selector) + __selectors__ = (one_line_rset, match_search_state, accept) accepts = ('Any',) search_states = ('linksearch',) @@ -793,7 +801,7 @@ rset, vid, divid, paginate = self.filter_box_context_info() self.w(u'
' % divid) self.pagination(self.req, rset, w=self.w) - self.wview(vid, rset) + self.wview(vid, rset, 'noresult') self.w(u'
') @cached @@ -842,7 +850,7 @@ """ id = 'editrelation' - __selectors__ = (req_form_params_selector,) + __selectors__ = (match_form_params,) form_params = ('rtype',) # TODO: inlineview, multiple edit, (widget view ?) @@ -855,13 +863,8 @@ assert rtype is not None, "rtype is mandatory for 'edirelation' view" targettype = self.req.form.get('targettype', targettype) role = self.req.form.get('role', role) - mode = entity.rtags.get_mode(rtype, targettype, role) - if mode == 'create': - return category = entity.rtags.get_category(rtype, targettype, role) - if category in ('generated', 'metadata'): - return - elif category in ('primary', 'secondary'): + if category in ('primary', 'secondary') or self.schema.rschema(rtype).is_final(): if hasattr(entity, '%s_format' % rtype): formatwdg = entity.get_widget('%s_format' % rtype, role) self.w(formatwdg.edit_render(entity)) @@ -872,10 +875,8 @@ self.w(u'%s %s %s' % (wdg.render_error(entity), wdg.edit_render(entity), wdg.render_help(entity),)) - elif category == 'generic': + else: self._render_generic_relation(entity, rtype, role) - else: - self.error("oops, wrong category %s", category) def _render_generic_relation(self, entity, relname, role): text = self.req.__('add %s %s %s' % (entity.e_schema, relname, role)) diff -r a81d3babb582 -r 85eae6a79f75 web/views/bookmark.py --- a/web/views/bookmark.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/bookmark.py Wed Jun 03 19:42:23 2009 +0200 @@ -68,9 +68,9 @@ dlink = u'[-]' % ( bookmark.eid, _('delete this bookmark')) label = '%s %s' % (dlink, label) - box.append(RawBoxItem(label, liclass=u'invisible')) + box.append(RawBoxItem(label)) if eschema.has_perm(req, 'add') and rschema.has_perm(req, 'add', toeid=ueid): - boxmenu = BoxMenu(req._('manage bookmarks'), liclass=u'invisible') + boxmenu = BoxMenu(req._('manage bookmarks')) linkto = 'bookmarked_by:%s:subject' % ueid # use a relative path so that we can move the application without # loosing bookmarks diff -r a81d3babb582 -r 85eae6a79f75 web/views/boxes.py --- a/web/views/boxes.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/boxes.py Wed Jun 03 19:42:23 2009 +0200 @@ -10,14 +10,14 @@ * startup views box :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" from logilab.mtconverter import html_escape -from cubicweb.common.selectors import (rset_selector, appobject_selectable) +from cubicweb.common.selectors import any_rset, appobject_selectable from cubicweb.web.htmlwidgets import BoxWidget, BoxMenu, BoxHtml, RawBoxItem from cubicweb.web.box import BoxTemplate, ExtResourcesBoxTemplate @@ -29,7 +29,7 @@ box with all actions impacting the entity displayed: edit, copy, delete change state, add related entities """ - __selectors__ = (rset_selector,) + BoxTemplate.__selectors__ + __selectors__ = (any_rset,) + BoxTemplate.__selectors__ id = 'edit_box' title = _('actions') order = 2 @@ -143,7 +143,7 @@ - + """ @@ -175,17 +175,16 @@ def call(self, **kwargs): box = BoxWidget(self.req._(self.title), self.id) - actions = [v for v in self.vreg.possible_views(self.req, self.rset) - if v.category != 'startupview'] - for category, actions in self.sort_actions(actions): + views = [v for v in self.vreg.possible_views(self.req, self.rset) + if v.category != 'startupview'] + for category, views in self.sort_actions(views): menu = BoxMenu(category) - for action in actions: - menu.append(self.box_action(action)) + for view in views: + menu.append(self.box_action(view)) box.append(menu) if not box.is_empty(): box.render(self.w) - class RSSIconBox(ExtResourcesBoxTemplate): """just display the RSS icon on uniform result set""" @@ -200,25 +199,8 @@ urlgetter = self.vreg.select_component('rss_feed_url', self.req, self.rset) url = urlgetter.feed_url() rss = self.req.external_resource('RSS_LOGO') - self.w(u'\n' % (html_escape(url), rss)) - + self.w(u'rss\n' % (html_escape(url), rss)) -## warning("schemabox ne marche plus pour le moment") -## class SchemaBox(BoxTemplate): -## """display a box containing link to list of entities by type""" -## id = 'schema_box' -## visible = False # disabled by default -## title = _('entity list') -## order = 60 - -## def call(self, **kwargs): -## box = BoxWidget(self.req._(title), self.id) -## for etype in self.config.etypes(self.req.user, 'read'): -## view = self.vreg.select_view('list', self.req, self.etype_rset(etype)) -## box.append(self.mk_action(display_name(self.req, etype, 'plural'), -## view.url(), etype=etype)) -## if not box.is_empty(): -## box.render(self.w) class StartupViewsBox(BoxTemplate): """display a box containing links to all startup views""" diff -r a81d3babb582 -r 85eae6a79f75 web/views/calendar.py --- a/web/views/calendar.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/calendar.py Wed Jun 03 19:42:23 2009 +0200 @@ -15,7 +15,7 @@ from cubicweb.interfaces import ICalendarable from cubicweb.common.utils import date_range from cubicweb.common.uilib import ajax_replace_url -from cubicweb.common.selectors import interface_selector +from cubicweb.common.selectors import implement_interface from cubicweb.common.registerers import priority_registerer from cubicweb.common.view import EntityView @@ -83,7 +83,7 @@ Does apply to ICalendarable compatible entities """ __registerer__ = priority_registerer - __selectors__ = (interface_selector,) + __selectors__ = (implement_interface,) accepts_interfaces = (ICalendarable,) need_navigation = False content_type = 'text/calendar' @@ -114,11 +114,11 @@ Does apply to ICalendarable compatible entities """ __registerer__ = priority_registerer - __selectors__ = (interface_selector,) + __selectors__ = (implement_interface,) accepts_interfaces = (ICalendarable,) need_navigation = False title = _('hCalendar') - templatable = False + #templatable = False id = 'hcal' def call(self): @@ -146,7 +146,7 @@ class OneMonthCal(EntityView): """At some point, this view will probably replace ampm calendars""" __registerer__ = priority_registerer - __selectors__ = (interface_selector, ) + __selectors__ = (implement_interface, ) accepts_interfaces = (ICalendarable,) need_navigation = False id = 'onemonthcal' @@ -331,7 +331,7 @@ class OneWeekCal(EntityView): """At some point, this view will probably replace ampm calendars""" __registerer__ = priority_registerer - __selectors__ = (interface_selector, ) + __selectors__ = (implement_interface, ) accepts_interfaces = (ICalendarable,) need_navigation = False id = 'oneweekcal' diff -r a81d3babb582 -r 85eae6a79f75 web/views/embedding.py --- a/web/views/embedding.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/embedding.py Wed Jun 03 19:42:23 2009 +0200 @@ -18,7 +18,7 @@ from cubicweb.interfaces import IEmbedable from cubicweb.common.uilib import soup2xhtml from cubicweb.common.selectors import (one_line_rset, score_entity_selector, - match_search_state, interface_selector) + match_search_state, implement_interface) from cubicweb.common.view import NOINDEX, NOFOLLOW from cubicweb.web.controller import Controller from cubicweb.web.action import Action @@ -82,7 +82,7 @@ id = 'embed' controller = 'embed' __selectors__ = (one_line_rset, match_search_state, - interface_selector, score_entity_selector) + implement_interface, score_entity_selector) accepts_interfaces = (IEmbedable,) title = _('embed') diff -r a81d3babb582 -r 85eae6a79f75 web/views/euser.py --- a/web/views/euser.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/euser.py Wed Jun 03 19:42:23 2009 +0200 @@ -1,17 +1,18 @@ """Specific views for users :organization: Logilab -:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" from logilab.common.decorators import cached +from logilab.mtconverter import html_escape from cubicweb.schema import display_name from cubicweb.web import INTERNAL_FIELD_VALUE from cubicweb.web.form import EntityForm -from cubicweb.web.views.baseviews import PrimaryView +from cubicweb.web.views.baseviews import PrimaryView, EntityView class EUserPrimaryView(PrimaryView): accepts = ('EUser',) @@ -31,8 +32,48 @@ return rschema.type in ['interested_in', 'tags', 'todo_by', 'bookmarked_by', ] +class FoafView(EntityView): + id = 'foaf' + accepts = ('EUser',) + title = _('foaf') + templatable = False + content_type = 'text/xml' + def call(self): + self.w(''' + '''% self.req.encoding) + for i in xrange(self.rset.rowcount): + self.cell_call(i, 0) + self.w(u'\n') + def cell_call(self, row, col): + entity = self.complete_entity(row, col) + self.w(u''' + + + ''' % (entity.absolute_url(), entity.absolute_url())) + + self.w(u'\n' % entity.eid) + self.w(u'%s\n' % html_escape(entity.dc_long_title())) + if entity.surname: + self.w(u'%s\n' + % html_escape(entity.surname)) + if entity.firstname: + self.w(u'%s\n' + % html_escape(entity.firstname)) + emailaddr = entity.get_email() + if emailaddr: + self.w(u'%s\n' % html_escape(emailaddr)) + self.w(u'\n') + +class FoafUsableView(FoafView): + id = 'foaf_usable' + + def call(self): + self.cell_call(0, 0) + class EditGroups(EntityForm): """displays a simple euser / egroups editable table""" diff -r a81d3babb582 -r 85eae6a79f75 web/views/facets.py --- a/web/views/facets.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/facets.py Wed Jun 03 19:42:23 2009 +0200 @@ -1,7 +1,7 @@ """the facets box and some basic facets :organization: Logilab -:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ __docformat__ = "restructuredtext en" @@ -10,9 +10,9 @@ from logilab.mtconverter import html_escape -from cubicweb.common.selectors import (chainfirst, chainall, nfentity_selector, - two_lines_rset, contextprop_selector, - yes, one_has_relation_selector) +from cubicweb.common.selectors import (chainfirst, chainall, non_final_entity, + two_lines_rset, match_context_prop, + yes, one_has_relation) from cubicweb.web.box import BoxTemplate from cubicweb.web.facet import (AbstractFacet, VocabularyFacet, FacetStringWidget, RelationFacet, prepare_facets_rqlst, filter_hiddens) @@ -28,8 +28,8 @@ """filter results of a query""" id = 'filter_box' __selectors__ = (chainfirst(contextview_selector, - chainall(nfentity_selector, two_lines_rset)), - contextprop_selector) + chainall(non_final_entity, two_lines_rset)), + match_context_prop) context = 'left' title = _('boxes_filter_box') visible = True # functionality provided by the search box by default @@ -153,7 +153,7 @@ class HasTextFacet(AbstractFacet): - __selectors__ = (one_has_relation_selector, contextprop_selector) + __selectors__ = (one_has_relation, match_context_prop) id = 'has_text-facet' rtype = 'has_text' role = 'subject' diff -r a81d3babb582 -r 85eae6a79f75 web/views/ibreadcrumbs.py --- a/web/views/ibreadcrumbs.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/ibreadcrumbs.py Wed Jun 03 19:42:23 2009 +0200 @@ -9,8 +9,8 @@ from logilab.mtconverter import html_escape from cubicweb.interfaces import IBreadCrumbs -from cubicweb.common.selectors import (contextprop_selector, one_line_rset, - interface_selector) +from cubicweb.common.selectors import (match_context_prop, one_line_rset, + implement_interface) from cubicweb.common.view import EntityView from cubicweb.common.uilib import cut # don't use AnyEntity since this may cause bug with isinstance() due to reloading @@ -29,7 +29,7 @@ # register msg not generated since no entity implements IPrevNext in cubicweb itself title = _('contentnavigation_breadcrumbs') help = _('contentnavigation_breadcrumbs_description') - __selectors__ = (one_line_rset, contextprop_selector, interface_selector) + __selectors__ = (one_line_rset, match_context_prop, implement_interface) accepts_interfaces = (IBreadCrumbs,) context = 'navtop' order = 5 @@ -73,7 +73,7 @@ class BreadCrumbComponent(BreadCrumbEntityVComponent): __registry__ = 'components' - __selectors__ = (one_line_rset, interface_selector) + __selectors__ = (one_line_rset, implement_interface) visible = True diff -r a81d3babb582 -r 85eae6a79f75 web/views/idownloadable.py --- a/web/views/idownloadable.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/idownloadable.py Wed Jun 03 19:42:23 2009 +0200 @@ -11,7 +11,7 @@ from cubicweb.interfaces import IDownloadable from cubicweb.common.mttransforms import ENGINE from cubicweb.common.selectors import (one_line_rset, score_entity_selector, - interface_selector, contextprop_selector) + implement_interface, match_context_prop) from cubicweb.web.box import EntityBoxTemplate from cubicweb.web.views import baseviews @@ -35,7 +35,7 @@ class DownloadBox(EntityBoxTemplate): id = 'download_box' - __selectors__ = (one_line_rset, interface_selector, contextprop_selector) + __selectors__ = (one_line_rset, implement_interface, match_context_prop) accepts_interfaces = (IDownloadable,) order = 10 def cell_call(self, row, col, title=None, label=None, **kwargs): @@ -48,7 +48,7 @@ of entities providing the necessary interface """ id = 'download' - __selectors__ = (one_line_rset, interface_selector) + __selectors__ = (one_line_rset, implement_interface) accepts_interfaces = (IDownloadable,) templatable = False @@ -77,7 +77,7 @@ """view displaying a link to download the file""" id = 'downloadlink' title = None # should not be listed in possible views - __selectors__ = (interface_selector,) + __selectors__ = (implement_interface,) accepts_interfaces = (IDownloadable,) @@ -89,7 +89,7 @@ class IDownloadablePrimaryView(baseviews.PrimaryView): - __selectors__ = (interface_selector,) + __selectors__ = (implement_interface,) #skip_attrs = ('eid', 'data',) # XXX accepts_interfaces = (IDownloadable,) @@ -122,7 +122,7 @@ class IDownloadableLineView(baseviews.OneLineView): - __selectors__ = (interface_selector,) + __selectors__ = (implement_interface,) # don't kick default oneline view accepts_interfaces = (IDownloadable,) @@ -138,7 +138,7 @@ class ImageView(baseviews.EntityView): - __selectors__ = (interface_selector, score_entity_selector) + __selectors__ = (implement_interface, score_entity_selector) id = 'image' title = _('image') accepts_interfaces = (IDownloadable,) diff -r a81d3babb582 -r 85eae6a79f75 web/views/igeocodable.py --- a/web/views/igeocodable.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/igeocodable.py Wed Jun 03 19:42:23 2009 +0200 @@ -4,7 +4,7 @@ from cubicweb.interfaces import IGeocodable from cubicweb.common.view import EntityView -from cubicweb.common.selectors import interface_selector +from cubicweb.common.selectors import implement_interface class GeocodingJsonView(EntityView): id = 'geocoding-json' @@ -12,10 +12,11 @@ templatable = False content_type = 'application/json' - __selectors__ = (interface_selector,) + __selectors__ = (implement_interface,) accepts_interfaces = (IGeocodable,) - + def call(self): + zoomlevel = self.req.form.pop('zoomlevel', 8) extraparams = self.req.form.copy() extraparams.pop('vid', None) extraparams.pop('rql', None) @@ -26,40 +27,41 @@ 'longitude': sum(marker['longitude'] for marker in markers) / len(markers), } geodata = { + 'zoomlevel': int(zoomlevel), 'center': center, 'markers': markers, } self.w(simplejson.dumps(geodata)) - + def build_marker_data(self, row, extraparams): entity = self.entity(row, 0) return {'latitude': entity.latitude, 'longitude': entity.longitude, 'title': entity.dc_long_title(), #icon defines : (icon._url, icon.size, icon.iconAncho', icon.shadow) - 'icon': entity.marker_icon() or (self.req.external_resource('GMARKER_ICON'), (20, 34), (4, 34), None), + 'icon': entity.marker_icon() or (self.req.external_resource('GMARKER_ICON'), (20, 34), (4, 34), None), 'bubbleUrl': entity.absolute_url(vid='gmap-bubble', __notemplate=1, **extraparams), } class GoogleMapBubbleView(EntityView): id = 'gmap-bubble' - - __selectors__ = (interface_selector,) + + __selectors__ = (implement_interface,) accepts_interfaces = (IGeocodable,) - + def cell_call(self, row, col): entity = self.entity(row, col) self.w(u'
%s
' % entity.view('oneline')) # FIXME: we should call something like address-view if available - + class GoogleMapsView(EntityView): id = 'gmap-view' - - __selectors__ = (interface_selector,) + + __selectors__ = (implement_interface,) accepts_interfaces = (IGeocodable,) need_navigation = False - + def call(self, gmap_key, width=400, height=400, uselabel=True, urlparams=None): self.req.add_js('http://maps.google.com/maps?file=api&v=2&key=%s' % gmap_key, localfile=False); @@ -70,13 +72,13 @@ else: loadurl = self.build_url(rql=rql, vid='geocoding-json', **urlparams) self.w(u'
' % (width, height, loadurl, uselabel)) - + class GoogeMapsLegend(EntityView): id = 'gmap-legend' - + def call(self): self.w(u'
    ') for rowidx in xrange(len(self.rset)): diff -r a81d3babb582 -r 85eae6a79f75 web/views/iprogress.py --- a/web/views/iprogress.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/iprogress.py Wed Jun 03 19:42:23 2009 +0200 @@ -12,7 +12,7 @@ from cubicweb.interfaces import IProgress, IMileStone from cubicweb.schema import display_name from cubicweb.common.view import EntityView -from cubicweb.common.selectors import interface_selector, accept_selector +from cubicweb.common.selectors import implement_interface, accept from cubicweb.web.htmlwidgets import ProgressBarWidget @@ -35,7 +35,7 @@ id = 'progress_table_view' title = _('task progression') - __selectors__ = (accept_selector, interface_selector) + __selectors__ = (accept, implement_interface) accepts_interfaces = (IMileStone,) @@ -182,7 +182,7 @@ """displays a progress bar""" id = 'progressbar' title = _('progress bar') - __selectors__ = (accept_selector, interface_selector) + __selectors__ = (accept, implement_interface) accepts_interfaces = (IProgress,) diff -r a81d3babb582 -r 85eae6a79f75 web/views/management.py --- a/web/views/management.py Thu Jan 15 10:13:25 2009 +0100 +++ b/web/views/management.py Wed Jun 03 19:42:23 2009 +0200 @@ -13,17 +13,16 @@ from cubicweb.common.utils import UStringIO from cubicweb.common.view import AnyRsetView, StartupView, EntityView -from cubicweb.common.uilib import (html_traceback, rest_traceback, html_escape, - toggle_link) +from cubicweb.common.uilib import html_traceback, rest_traceback from cubicweb.common.selectors import (yes, one_line_rset, - accept_rset_selector, none_rset, - chainfirst, chainall) + accept_rset, none_rset, + chainfirst, chainall) from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param, stdmsgs from cubicweb.web.widgets import StaticComboBoxWidget from cubicweb.web.form import FormMixIn _ = unicode - + def begin_form(w, entity, redirectvid, redirectpath=None, msg=None): w(u'
    \n' % entity.req.build_url('edit')) w(u'
    \n') @@ -42,10 +41,10 @@ """display security information for a given entity""" id = 'security' title = _('security') - + def cell_call(self, row, col): - self.req.add_js('cubicweb.edition.js') - self.req.add_css('cubicweb.acl.css') + self.req.add_js('cubicweb.edition.js') + self.req.add_css('cubicweb.acl.css') entity = self.entity(row, col) w = self.w _ = self.req._ @@ -90,7 +89,7 @@ w(u'%s' % u'
    '.join(expr.expression for expr in rqlexprs)) w(u'\n') w(u'') - + def owned_by_edit_form(self, entity): self.w('

    %s

    ' % self.req._('ownership')) begin_form(self.w, entity, 'security', msg= _('ownerships have been changed')) @@ -169,7 +168,7 @@ wdg = newperm.get_widget('label') w(u'%s\n' % wdg.edit_render(newperm)) wdg = newperm.get_widget('require_group') - w(u'%s\n' % wdg.edit_render(newperm)) + w(u'%s\n' % wdg.edit_render(newperm)) w(u'%s\n' % self.button_ok()) w(u'') w(u'
    \n') @@ -178,12 +177,12 @@ return (u'' % self.req._(stdmsgs.BUTTON_OK)) - + class ErrorView(AnyRsetView): """default view when no result has been found""" __selectors__ = (yes,) id = 'error' - + def page_title(self): """returns a title according to the result set - used for the title in the HTML header @@ -192,11 +191,11 @@ def call(self): req = self.req.reset_headers() - _ = req._ + _ = req._; w = self.w ex = req.data.get('ex')#_("unable to find exception information")) excinfo = req.data.get('excinfo') title = _('an error occured') - self.w(u'

    %s

    ' % title) + w(u'

    %s

    ' % title) if 'errmsg' in req.data: ex = req.data['errmsg'] exclass = None @@ -205,53 +204,53 @@ ex = exc_message(ex, req.encoding) if excinfo is not None and self.config['print-traceback']: if exclass is None: - self.w(u'
    %s
    ' + w(u'
    %s
    ' % html_escape(ex).replace("\n","
    ")) else: - self.w(u'
    %s: %s
    ' + w(u'
    %s: %s
    ' % (exclass, html_escape(ex).replace("\n","
    "))) - self.w(u'
    ') - self.w(u'
    %s
    ' % html_traceback(excinfo, ex, '')) + w(u'
    ') + w(u'
    %s
    ' % html_traceback(excinfo, ex, '')) else: - self.w(u'
    %s
    ' % (html_escape(ex).replace("\n","
    "))) + w(u'
    %s
    ' % (html_escape(ex).replace("\n","
    "))) # if excinfo is not None, it's probably not a bug if excinfo is None: return vcconf = self.config.vc_config() - self.w(u"
    ") + w(u"
    ") eversion = vcconf.get('cubicweb', _('no version information')) # NOTE: tuple wrapping needed since eversion is itself a tuple - self.w(u"CubicWeb version: %s
    \n" % (eversion,)) + w(u"CubicWeb version: %s
    \n" % (eversion,)) for pkg in self.config.cubes(): pkgversion = vcconf.get(pkg, _('no version information')) - self.w(u"Package %s version: %s
    \n" % (pkg, pkgversion)) - self.w(u"
    ") + w(u"Package %s version: %s
    \n" % (pkg, pkgversion)) + w(u"
    ") # creates a bug submission link if SUBMIT_URL is set submiturl = self.config['submit-url'] if submiturl: binfo = text_error_description(ex, excinfo, req, eversion, [(pkg, vcconf.get(pkg, _('no version information'))) for pkg in self.config.cubes()]) - self.w(u'
    \n' % html_escape(submiturl)) - self.w(u'
    \n') - self.w(u'' % html_escape(binfo)) - self.w(u'') - self.w(u'') - self.w(u'' % _('Submit bug report')) - self.w(u'
    \n') - self.w(u'
    \n') + w(u'
    \n' % html_escape(submiturl)) + w(u'
    \n') + w(u'' % html_escape(binfo)) + w(u'') + w(u'') + w(u'' % _('Submit bug report')) + w(u'
    \n') + w(u'
    \n') submitmail = self.config['submit-mail'] if submitmail: binfo = text_error_description(ex, excinfo, req, eversion, [(pkg, vcconf.get(pkg, _('no version information'))) for pkg in self.config.cubes()]) - self.w(u'
    \n' % req.build_url('reportbug')) - self.w(u'
    \n') - self.w(u'' % html_escape(binfo)) - self.w(u'') - self.w(u'' % _('Submit bug report by mail')) - self.w(u'
    \n') - self.w(u'
    \n') + w(u'
    \n' % req.build_url('reportbug')) + w(u'
    \n') + w(u'' % html_escape(binfo)) + w(u'') + w(u'' % _('Submit bug report by mail')) + w(u'
    \n') + w(u'
    \n') def exc_message(ex, encoding): @@ -262,7 +261,7 @@ return unicode(str(ex), encoding, 'replace') except: return unicode(repr(ex), encoding, 'replace') - + def text_error_description(ex, excinfo, req, eversion, cubes): binfo = rest_traceback(excinfo, html_escape(ex)) binfo += u'\n\n:URL: %s\n' % req.url() @@ -284,23 +283,48 @@ _('components') _('contentnavigation') + +def make_togglable_link(nodeid, label, cookiename): + """builds a HTML link that switches the visibility & remembers it""" + action = u"javascript: toggle_and_remember_visibility('%s', '%s')" % \ + (nodeid, cookiename) + return u'%s' % (action, label) + +def css_class(someclass): + return someclass and 'class="%s"' % someclass or '' + class SystemEpropertiesForm(FormMixIn, StartupView): controller = 'edit' id = 'systemepropertiesform' title = _('site configuration') require_groups = ('managers',) category = 'startupview' - + def linkable(self): return True - + def url(self): """return the url associated with this view. We can omit rql here""" return self.build_url('view', vid=self.id) - + + def _cookie_name(self, somestr): + return str('%s_property_%s' % (self.config.appid, somestr)) + + def _group_status(self, group, default=u'hidden'): + cookies = self.req.get_cookie() + cookiename = self._cookie_name(group) + cookie = cookies.get(cookiename) + if cookie is None: + cookies[cookiename] = default + self.req.set_cookie(cookies, cookiename, maxage=None) + status = default + else: + status = cookie.value + return status + def call(self, **kwargs): """The default view representing the application's index""" - self.req.add_js('cubicweb.edition.js') + self.req.add_js(('cubicweb.edition.js', 'cubicweb.preferences.js')) self.req.add_css('cubicweb.preferences.css') vreg = self.vreg values = self.defined_keys @@ -322,7 +346,7 @@ for group, objects in groupedopts.items(): for oid, keys in objects.items(): groupedopts[group][oid] = self.form(keys, True) - + w = self.w req = self.req _ = req._ @@ -330,17 +354,21 @@ w(self.error_message()) for label, group, form in sorted((_(g), g, f) for g, f in mainopts.iteritems()): + status = css_class(self._group_status(group)) #'hidden' (collapsed), or '' (open) ? w(u'

    %s

    \n' % - (toggle_link('fieldset_' + group, label))) - w(u'