# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1236024234 -3600 # Node ID 6a25c58a1c237b1a376453a9105f427073ec9a9b # Parent 536e421b082bb496dd468d41775ee00ed2018c78# Parent 93447d75c4b93de61d0b079f35da50a5a2150e14 backport stable branch, take care a lot of conflicts occured, this may be the revision you're looking for... diff -r 536e421b082b -r 6a25c58a1c23 .hgtags --- a/.hgtags Mon Mar 02 20:44:14 2009 +0100 +++ b/.hgtags Mon Mar 02 21:03:54 2009 +0100 @@ -16,3 +16,5 @@ fc222bc99929d395c1c2235c40d3bb6f247b4ba9 cubicweb-debian-version-3_0_4-1 7ad527099393ef56f27af313392022bb8ed73082 cubicweb-version-3_0_9 a8e9e53b245d53838a07aa8c76d1bed352692a9f cubicweb-debian-version-3_0_9-1 +a711c7c185d15a1bd22b7eaab46a26b98b74fbf3 cubicweb-version-3_1_0 +dd3efdf58d281286d6f52f7416db349b75b7789c cubicweb-debian-version-3_1_0-1 diff -r 536e421b082b -r 6a25c58a1c23 __pkginfo__.py --- a/__pkginfo__.py Mon Mar 02 20:44:14 2009 +0100 +++ b/__pkginfo__.py Mon Mar 02 21:03:54 2009 +0100 @@ -6,7 +6,7 @@ distname = "cubicweb" modname = "cubicweb" -numversion = (3, 0, 10) +numversion = (3, 1, 0) version = '.'.join(str(num) for num in numversion) license = 'GPL' diff -r 536e421b082b -r 6a25c58a1c23 common/entity.py diff -r 536e421b082b -r 6a25c58a1c23 common/mixins.py --- a/common/mixins.py Mon Mar 02 20:44:14 2009 +0100 +++ b/common/mixins.py Mon Mar 02 21:03:54 2009 +0100 @@ -175,14 +175,14 @@ return self.req._(self.state) def wf_state(self, statename): - rset = self.req.execute('Any S, SN WHERE S name %(n)s, S state_of E, E name %(e)s', + rset = self.req.execute('Any S, SN WHERE S name SN, S name %(n)s, S state_of E, E name %(e)s', {'n': statename, 'e': str(self.e_schema)}) if rset: return rset.get_entity(0, 0) return None def wf_transition(self, trname): - rset = self.req.execute('Any T, TN WHERE T name %(n)s, T transition_of E, E name %(e)s', + rset = self.req.execute('Any T, TN WHERE T name TN, T name %(n)s, T transition_of E, E name %(e)s', {'n': trname, 'e': str(self.e_schema)}) if rset: return rset.get_entity(0, 0) @@ -368,22 +368,18 @@ """provide default implementations for IProgress interface methods""" @property - @cached def cost(self): return self.progress_info()['estimated'] @property - @cached def revised_cost(self): return self.progress_info().get('estimatedcorrected', self.cost) @property - @cached def done(self): return self.progress_info()['done'] @property - @cached def todo(self): return self.progress_info()['todo'] @@ -402,6 +398,6 @@ return 100. * self.done / self.revised_cost except ZeroDivisionError: # total cost is 0 : if everything was estimated, task is completed - if self.progress_info().get('notestmiated'): + if self.progress_info().get('notestimated'): return 0. return 100 diff -r 536e421b082b -r 6a25c58a1c23 common/test/unittest_mixins.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/common/test/unittest_mixins.py Mon Mar 02 21:03:54 2009 +0100 @@ -0,0 +1,25 @@ +from logilab.common.testlib import unittest_main +from cubicweb.devtools.apptest import EnvBasedTC + +class WorkfloableMixInTC(EnvBasedTC): + def test_wf_state(self): + s = self.add_entity('State', name=u'activated') + self.execute('SET X state_of ET WHERE ET name "Bookmark", X eid %(x)s', + {'x': s.eid}) + es = self.user().wf_state('activated') + self.assertEquals(es.state_of[0].name, 'EUser') + + def test_wf_transition(self): + t = self.add_entity('Transition', name=u'deactivate') + self.execute('SET X transition_of ET WHERE ET name "Bookmark", X eid %(x)s', + {'x': t.eid}) + et = self.user().wf_transition('deactivate') + self.assertEquals(et.transition_of[0].name, 'EUser') + + def test_change_state(self): + user = self.user() + user.change_state(user.wf_state('deactivated').eid) + self.assertEquals(user.state, 'deactivated') + +if __name__ == '__main__': + unittest_main() diff -r 536e421b082b -r 6a25c58a1c23 common/uilib.py --- a/common/uilib.py Mon Mar 02 20:44:14 2009 +0100 +++ b/common/uilib.py Mon Mar 02 21:03:54 2009 +0100 @@ -179,7 +179,7 @@ if text is None: return u'' words = text.split() - text = ' '.join(words) # normalize spaces + text = u' '.join(words) # normalize spaces minlength = len(' '.join(words[:nbwords])) textlength = text.find('.', minlength) + 1 if textlength == 0: # no point found diff -r 536e421b082b -r 6a25c58a1c23 common/view.py diff -r 536e421b082b -r 6a25c58a1c23 debian/changelog --- a/debian/changelog Mon Mar 02 20:44:14 2009 +0100 +++ b/debian/changelog Mon Mar 02 21:03:54 2009 +0100 @@ -1,3 +1,9 @@ +cubicweb (3.1.0-1) unstable; urgency=low + + * new upstream release + + -- Sylvain Thénault Wed, 25 Feb 2009 18:41:47 +0100 + cubicweb (3.0.10-1) unstable; urgency=low * merge cubicweb-core package into cubicweb-common diff -r 536e421b082b -r 6a25c58a1c23 debian/control --- a/debian/control Mon Mar 02 20:44:14 2009 +0100 +++ b/debian/control Mon Mar 02 21:03:54 2009 +0100 @@ -59,7 +59,7 @@ Package: cubicweb-web Architecture: all XB-Python-Version: ${python:Versions} -Depends: ${python:Depends}, cubicweb-common (= ${source:Version}), python-simplejson (>= 1.3), python-docutils, python-vobject, python-elementtree +Depends: ${python:Depends}, cubicweb-common (= ${source:Version}), python-docutils, python-vobject, python-elementtree Recommends: fckeditor Description: web interface library for the CubicWeb framework CubicWeb is a semantic web application framework. @@ -74,7 +74,7 @@ Package: cubicweb-common Architecture: all XB-Python-Version: ${python:Versions} -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) +Depends: ${python:Depends}, python-logilab-mtconverter (>= 0.6.0), python-simpletal (>= 4.0), graphviz, gettext, python-lxml, python-logilab-common (>= 0.38.1), python-yams (>= 0.20.2), python-rql (>= 0.20.2), python-simplejson (>= 1.3) Recommends: python-psyco Conflicts: cubicweb-core Replaces: cubicweb-core diff -r 536e421b082b -r 6a25c58a1c23 devtools/__init__.py --- a/devtools/__init__.py Mon Mar 02 20:44:14 2009 +0100 +++ b/devtools/__init__.py Mon Mar 02 21:03:54 2009 +0100 @@ -260,6 +260,8 @@ - http://www.sqlite.org/cvstrac/tktview?tn=1327,33 (some dates are returned as strings rather thant date objects) """ + if hasattr(querier.__class__, '_devtools_sqlite_patched'): + return # already monkey patched def wrap_execute(base_execute): def new_execute(*args, **kwargs): rset = base_execute(*args, **kwargs) @@ -288,7 +290,7 @@ return rset return new_execute querier.__class__.execute = wrap_execute(querier.__class__.execute) - + querier.__class__._devtools_sqlite_patched = True def init_test_database(driver='sqlite', configdir='data', config=None, vreg=None): diff -r 536e421b082b -r 6a25c58a1c23 devtools/apptest.py --- a/devtools/apptest.py Mon Mar 02 20:44:14 2009 +0100 +++ b/devtools/apptest.py Mon Mar 02 21:03:54 2009 +0100 @@ -407,6 +407,7 @@ self.__close(self.cnxid) # other utilities ######################################################### + def set_debug(self, debugmode): from cubicweb.server import set_debug set_debug(debugmode) @@ -452,7 +453,7 @@ self.__commit = repo.commit self.__rollback = repo.rollback self.__close = repo.close - self.cnxid = repo.connect(*self.default_user_password()) + self.cnxid = self.cnx.sessionid self.session = repo._sessions[self.cnxid] # XXX copy schema since hooks may alter it and it may be not fully # cleaned (missing some schema synchronization support) diff -r 536e421b082b -r 6a25c58a1c23 entity.py --- a/entity.py Mon Mar 02 20:44:14 2009 +0100 +++ b/entity.py Mon Mar 02 21:03:54 2009 +0100 @@ -45,6 +45,8 @@ 'inlineview')) def __init__(self, eclass, tagdefs): + # XXX if a rtag is redefined in a subclass, + # the rtag of the base class overwrite the rtag of the subclass self.eclass = eclass self._tagdefs = {} for relation, tags in tagdefs.iteritems(): diff -r 536e421b082b -r 6a25c58a1c23 goa/dbmyams.py --- a/goa/dbmyams.py Mon Mar 02 20:44:14 2009 +0100 +++ b/goa/dbmyams.py Mon Mar 02 21:03:54 2009 +0100 @@ -109,6 +109,7 @@ super(GaeSchemaLoader, self).__init__(*args, **kwargs) self.defined = {} self.created = [] + self.loaded_files = [] self._instantiate_handlers() def finalize(self, register_base_types=False): diff -r 536e421b082b -r 6a25c58a1c23 i18n/en.po --- a/i18n/en.po Mon Mar 02 20:44:14 2009 +0100 +++ b/i18n/en.po Mon Mar 02 21:03:54 2009 +0100 @@ -47,10 +47,26 @@ msgstr "" #, python-format +msgid "%d days" +msgstr "" + +#, python-format +msgid "%d hours" +msgstr "" + +#, python-format +msgid "%d minutes" +msgstr "" + +#, python-format msgid "%d months" msgstr "" #, python-format +msgid "%d seconds" +msgstr "" + +#, python-format msgid "%d weeks" msgstr "" @@ -59,7 +75,31 @@ msgstr "" #, python-format -msgid "%s days" +msgid "%d days" +msgstr "" + +#, python-format +msgid "%d hours" +msgstr "" + +#, python-format +msgid "%d minutes" +msgstr "" + +#, python-format +msgid "%d months" +msgstr "" + +#, python-format +msgid "%d seconds" +msgstr "" + +#, python-format +msgid "%d weeks" +msgstr "" + +#, python-format +msgid "%d years" msgstr "" #, python-format @@ -67,14 +107,6 @@ msgstr "" #, python-format -msgid "%s hours" -msgstr "" - -#, python-format -msgid "%s minutes" -msgstr "" - -#, python-format msgid "%s not estimated" msgstr "" @@ -83,10 +115,6 @@ msgstr "" #, python-format -msgid "%s seconds" -msgstr "" - -#, python-format msgid "%s software version of the database" msgstr "" @@ -376,9 +404,6 @@ msgid "Problem occured" msgstr "" -msgid "Project linked data" -msgstr "" - msgid "RQLExpression" msgstr "RQL expression" @@ -2203,15 +2228,12 @@ msgid "ordernum" msgstr "order" -msgid "owl (tbox+abox)" +msgid "owl" msgstr "" msgid "owlabox" msgstr "" -msgid "owlaboxlight" -msgstr "" - msgid "owned_by" msgstr "owned by" @@ -2583,9 +2605,6 @@ msgid "task progression" msgstr "" -msgid "tbox" -msgstr "" - msgid "text" msgstr "" diff -r 536e421b082b -r 6a25c58a1c23 i18n/es.po --- a/i18n/es.po Mon Mar 02 20:44:14 2009 +0100 +++ b/i18n/es.po Mon Mar 02 21:03:54 2009 +0100 @@ -52,34 +52,66 @@ msgstr "%(subject)s %(etype)s #%(eid)s (%(login)s)" #, python-format +msgid "%d days" +msgstr "%d días" + +#, python-format +msgid "%d hours" +msgstr "%d horas" + +#, python-format +msgid "%d minutes" +msgstr "%d minutos" + +#, python-format msgid "%d months" msgstr "%d meses" #, python-format +msgid "%d seconds" +msgstr "%d segundos" + +#, python-format msgid "%d weeks" msgstr "%d semanas" #, python-format -msgid "%d years" -msgstr "%d años" +msgid "%d years" +msgstr "%d años" + +#, python-format +msgid "%d days" +msgstr "%d días" + +#, python-format +msgid "%d hours" +msgstr "%d horas" #, python-format -msgid "%s days" -msgstr "%d días" +msgid "%d minutes" +msgstr "%d minutos" + +#, python-format +msgid "%d months" +msgstr "%d meses" + +#, python-format +msgid "%d seconds" +msgstr "%d segundos" + +#, python-format +msgid "%d weeks" +msgstr "%d semanas" + +#, python-format +msgid "%d years" +msgstr "%d años" #, python-format msgid "%s error report" msgstr "%s reporte de errores" #, python-format -msgid "%s hours" -msgstr "%s horas" - -#, python-format -msgid "%s minutes" -msgstr "%s minutos" - -#, python-format msgid "%s not estimated" msgstr "%s no estimado(s)" @@ -88,10 +120,6 @@ msgstr "%s resultados de la demanda" #, python-format -msgid "%s seconds" -msgstr "%s segundos" - -#, python-format msgid "%s software version of the database" msgstr "version sistema de la base para %s" @@ -381,9 +409,6 @@ msgid "Problem occured" msgstr "Ha ocurrido un error" -msgid "Project linked data" -msgstr "" - msgid "RQLExpression" msgstr "Expresión RQL" @@ -2295,15 +2320,12 @@ msgid "ordernum" msgstr "ordre" -msgid "owl (tbox+abox)" +msgid "owl" msgstr "" msgid "owlabox" msgstr "" -msgid "owlaboxlight" -msgstr "" - msgid "owned_by" msgstr "appartient ‡" @@ -2688,9 +2710,6 @@ msgid "task progression" msgstr "avancement de la t‚che" -msgid "tbox" -msgstr "" - msgid "text" msgstr "text" diff -r 536e421b082b -r 6a25c58a1c23 i18n/fr.po --- a/i18n/fr.po Mon Mar 02 20:44:14 2009 +0100 +++ b/i18n/fr.po Mon Mar 02 21:03:54 2009 +0100 @@ -52,10 +52,26 @@ msgstr "%(subject)s %(etype)s #%(eid)s (%(login)s)" #, python-format +msgid "%d days" +msgstr "%d jours" + +#, python-format +msgid "%d hours" +msgstr "%d heures" + +#, python-format +msgid "%d minutes" +msgstr "%d minutes" + +#, python-format msgid "%d months" msgstr "%d mois" #, python-format +msgid "%d seconds" +msgstr "%d secondes" + +#, python-format msgid "%d weeks" msgstr "%d semaines" @@ -64,22 +80,38 @@ msgstr "%d années" #, python-format -msgid "%s days" -msgstr "%d jours" +msgid "%d days" +msgstr "%d jours" + +#, python-format +msgid "%d hours" +msgstr "%d heures" + +#, python-format +msgid "%d minutes" +msgstr "%d minutes" + +#, python-format +msgid "%d months" +msgstr "%d mois" + +#, python-format +msgid "%d seconds" +msgstr "%d secondes" + +#, python-format +msgid "%d weeks" +msgstr "%d semaines" + +#, python-format +msgid "%d years" +msgstr "%d années" #, python-format msgid "%s error report" msgstr "%s rapport d'erreur" #, python-format -msgid "%s hours" -msgstr "%s heures" - -#, python-format -msgid "%s minutes" -msgstr "%s minutes" - -#, python-format msgid "%s not estimated" msgstr "%s non estimé(s)" @@ -88,10 +120,6 @@ msgstr "%s résultats pour la requête" #, python-format -msgid "%s seconds" -msgstr "%s secondes" - -#, python-format msgid "%s software version of the database" msgstr "version logicielle de la base pour %s" @@ -381,9 +409,6 @@ msgid "Problem occured" msgstr "Une erreur est survenue" -msgid "Project linked data" -msgstr "" - msgid "RQLExpression" msgstr "Expression RQL" @@ -2299,15 +2324,12 @@ msgid "ordernum" msgstr "ordre" -msgid "owl (tbox+abox)" +msgid "owl" msgstr "" msgid "owlabox" msgstr "" -msgid "owlaboxlight" -msgstr "" - msgid "owned_by" msgstr "appartient à" @@ -2692,9 +2714,6 @@ msgid "task progression" msgstr "avancement de la tâche" -msgid "tbox" -msgstr "" - msgid "text" msgstr "text" diff -r 536e421b082b -r 6a25c58a1c23 server/migractions.py --- a/server/migractions.py Mon Mar 02 20:44:14 2009 +0100 +++ b/server/migractions.py Mon Mar 02 21:03:54 2009 +0100 @@ -57,7 +57,7 @@ self.repo_connect() if not schema: schema = config.load_schema(expand_cubes=True) - self.new_schema = schema + self.fs_schema = schema self._synchronized = set() @cached @@ -217,7 +217,9 @@ 'rql': self.rqlexec, 'rqliter': self.rqliter, 'schema': self.repo.schema, - 'newschema': self.new_schema, + # XXX deprecate + 'newschema': self.fs_schema, + 'fsschema': self.fs_schema, 'cnx': self.cnx, 'session' : self.session, 'repo' : self.repo, @@ -281,22 +283,22 @@ if not update_database: self.commit() return - with_new_cubes = self.config.load_schema() + newcubes_schema = 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 with_new_cubes.relations(): + for rschema in newcubes_schema.relations(): if not rschema in self.repo.schema: self.cmd_add_relation_type(rschema.type) new.add(rschema.type) - for eschema in with_new_cubes.entities(): + for eschema in newcubes_schema.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 with_new_cubes.relations(): + for rschema in newcubes_schema.relations(): existingschema = self.repo.schema.rschema(rschema.type) for (fromtype, totype) in rschema.iter_rdefs(): if existingschema.has_rdef(fromtype, totype): @@ -315,25 +317,25 @@ removedcubes = super(ServerMigrationHelper, self).cmd_remove_cube(cube) if not removedcubes: return - oldschema = self.new_schema - self.new_schema = newschema = self.config.load_schema() + fsschema = self.fs_schema + removedcubes_schema = self.config.load_schema() reposchema = self.repo.schema # execute pre-remove files for pack in reversed(removedcubes): self.exec_event_script('preremove', self.config.cube_dir(pack)) # remove cubes'entity and relation types - for rschema in oldschema.relations(): - if not rschema in newschema and rschema in reposchema: + for rschema in fsschema.relations(): + if not rschema in removedcubes_schema and rschema in reposchema: self.cmd_drop_relation_type(rschema.type) - for eschema in oldschema.entities(): - if not eschema in newschema and eschema in reposchema: + for eschema in fsschema.entities(): + if not eschema in removedcubes_schema and eschema in reposchema: self.cmd_drop_entity_type(eschema.type) - for rschema in oldschema.relations(): - if rschema in newschema and rschema in reposchema: + for rschema in fsschema.relations(): + if rschema in removedcubes_schema and rschema in reposchema: # check if attributes/relations has been added to entities from # other cubes for fromtype, totype in rschema.iter_rdefs(): - if not newschema[rschema.type].has_rdef(fromtype, totype) and \ + if not removedcubes_schema[rschema.type].has_rdef(fromtype, totype) and \ reposchema[rschema.type].has_rdef(fromtype, totype): self.cmd_drop_relation_definition( str(fromtype), rschema.type, str(totype)) @@ -349,7 +351,7 @@ def cmd_add_attribute(self, etype, attrname, attrtype=None, commit=True): """add a new attribute on the given entity type""" if attrtype is None: - rschema = self.new_schema.rschema(attrname) + rschema = self.fs_schema.rschema(attrname) attrtype = rschema.objects(etype)[0] self.cmd_add_relation_definition(etype, attrname, attrtype, commit=commit) @@ -368,7 +370,7 @@ `oldname` is a string giving the name of the existing attribute `newname` is a string giving the name of the renamed attribute """ - eschema = self.new_schema.eschema(etype) + eschema = self.fs_schema.eschema(etype) attrtype = eschema.destination(newname) # have to commit this first step anyway to get the definition # actually in the schema @@ -393,7 +395,7 @@ if eschema.is_final(): applschema.del_entity_type(etype) else: - eschema = self.new_schema.eschema(etype) + eschema = self.fs_schema.eschema(etype) confirm = self.verbosity >= 2 # register the entity into EEType self.rqlexecall(ss.eschema2rql(eschema), ask_confirm=confirm) @@ -499,7 +501,7 @@ committing depends on the `commit` argument value). """ - rschema = self.new_schema.rschema(rtype) + rschema = self.fs_schema.rschema(rtype) # register the relation into ERType and insert necessary relation # definitions self.rqlexecall(ss.rschema2rql(rschema, addrdef=False), @@ -537,7 +539,7 @@ """register a new relation definition, from its definition found in the schema definition file """ - rschema = self.new_schema.rschema(rtype) + rschema = self.fs_schema.rschema(rtype) if not rtype in self.repo.schema: self.cmd_add_relation_type(rtype, addrdef=False, commit=True) self.rqlexecall(ss.rdef2rql(rschema, subjtype, objtype), @@ -564,7 +566,7 @@ """permission synchronization for an entity or relation type""" if ertype in ('eid', 'has_text', 'identity'): return - newrschema = self.new_schema[ertype] + newrschema = self.fs_schema[ertype] teid = self.repo.schema[ertype].eid if 'update' in newrschema.ACTIONS or newrschema.is_final(): # entity type @@ -644,7 +646,7 @@ if rtype in self._synchronized: return self._synchronized.add(rtype) - rschema = self.new_schema.rschema(rtype) + rschema = self.fs_schema.rschema(rtype) self.rqlexecall(ss.updaterschema2rql(rschema), ask_confirm=self.verbosity>=2) reporschema = self.repo.schema.rschema(rtype) @@ -674,7 +676,7 @@ self._synchronized.add(etype) repoeschema = self.repo.schema.eschema(etype) try: - eschema = self.new_schema.eschema(etype) + eschema = self.fs_schema.eschema(etype) except KeyError: return repospschema = repoeschema.specializes() @@ -719,7 +721,7 @@ * constraints """ subjtype, objtype = str(subjtype), str(objtype) - rschema = self.new_schema.rschema(rtype) + rschema = self.fs_schema.rschema(rtype) reporschema = self.repo.schema.rschema(rschema) if (subjtype, rschema, objtype) in self._synchronized: return @@ -903,6 +905,13 @@ if commit: self.commit() + def cmd_set_state(self, eid, statename, commit=False): + self.session.set_pool() # ensure pool is set + entity = self.session.eid_rset(eid).get_entity(0, 0) + entity.change_state(entity.wf_state(statename).eid) + if commit: + self.commit() + # EProperty handling ###################################################### def cmd_property_value(self, pkey): diff -r 536e421b082b -r 6a25c58a1c23 server/rqlannotation.py --- a/server/rqlannotation.py Mon Mar 02 20:44:14 2009 +0100 +++ b/server/rqlannotation.py Mon Mar 02 21:03:54 2009 +0100 @@ -260,7 +260,6 @@ has_text_query = True return has_text_query - def is_ambiguous(self, var): # ignore has_text relation if len([rel for rel in var.stinfo['relations'] @@ -337,7 +336,7 @@ except KeyError: # no relation to deambiguify continue - + def _debug_print(self): print 'varsols', dict((x, sorted(str(v) for v in values)) for x, values in self.varsols.iteritems()) @@ -375,8 +374,9 @@ otheretypes = (other.uidtype,) deambiguifier = None if otheretypes is not None: - # unless types for variable are already non-ambigous, check - # if this relation has some type ambiguity + # to restrict, we must check that for all type in othertypes, + # possible types on the other end of the relation are matching + # variable's possible types rschema = self.rschema(rel.r_type) if onlhs: rtypefunc = rschema.subjects @@ -386,7 +386,8 @@ reltypes = frozenset(rtypefunc(otheretype)) if var.stinfo['possibletypes'] != reltypes: break - self.restrict(var, reltypes) + else: + self.restrict(var, var.stinfo['possibletypes']) self.deambification_map[var] = deambiguifier return True return False diff -r 536e421b082b -r 6a25c58a1c23 server/session.py diff -r 536e421b082b -r 6a25c58a1c23 server/sources/ldapuser.py --- a/server/sources/ldapuser.py Mon Mar 02 20:44:14 2009 +0100 +++ b/server/sources/ldapuser.py Mon Mar 02 21:03:54 2009 +0100 @@ -176,6 +176,10 @@ external repository """ self.info('synchronizing ldap source %s', self.uri) + try: + ldap_emailattr = self.user_rev_attrs['email'] + except KeyError: + return # no email in ldap, we're done session = self.repo.internal_session() try: cursor = session.system_sql("SELECT eid, extid FROM entities WHERE " @@ -184,7 +188,7 @@ # if no result found, _search automatically delete entity information res = self._search(session, extid, BASE) if res: - ldapemailaddr = res[0].get(self.user_rev_attrs['email']) + ldapemailaddr = res[0].get(ldap_emailattr) if ldapemailaddr: rset = session.execute('EmailAddress X,A WHERE ' 'U use_email X, U eid %(u)s', @@ -628,7 +632,10 @@ filter = '(%s%s)' % (self._ldap_attrs[relation.r_type], rhs.accept(self)) except KeyError: - assert relation.r_type == 'password' # 2.38 migration + # unsupported attribute + self.source.warning('%s source can\'t handle relation %s, no ' + 'results will be returned from this source', + self.source.uri, relation) raise UnknownEid # trick to return no result return filter diff -r 536e421b082b -r 6a25c58a1c23 server/sources/native.py --- a/server/sources/native.py Mon Mar 02 20:44:14 2009 +0100 +++ b/server/sources/native.py Mon Mar 02 21:03:54 2009 +0100 @@ -451,6 +451,7 @@ try: res = session.system_sql(sql).fetchone() except: + assert self.pool, 'session has no pool set' raise UnknownEid(eid) if res is None: raise UnknownEid(eid) diff -r 536e421b082b -r 6a25c58a1c23 server/test/data/schema/relations.rel --- a/server/test/data/schema/relations.rel Mon Mar 02 20:44:14 2009 +0100 +++ b/server/test/data/schema/relations.rel Mon Mar 02 21:03:54 2009 +0100 @@ -43,3 +43,6 @@ EPermission require_state State Note migrated_from Note + +Note attachment File +Note attachment Image diff -r 536e421b082b -r 6a25c58a1c23 server/test/unittest_ldapuser.py --- a/server/test/unittest_ldapuser.py Mon Mar 02 20:44:14 2009 +0100 +++ b/server/test/unittest_ldapuser.py Mon Mar 02 21:03:54 2009 +0100 @@ -33,8 +33,8 @@ repo, cnx = init_test_database('sqlite', config=config) class LDAPUserSourceTC(RepositoryBasedTC): - repo = repo - + repo, cnx = repo, cnx + def patch_authenticate(self): self._orig_authenticate = LDAPUserSource.authenticate LDAPUserSource.authenticate = nopwd_authenticate @@ -242,7 +242,10 @@ ['users', 'cochon'], ['users', 'syt']]) - + def test_cd_restriction(self): + rset = self.execute('EUser X WHERE X creation_date > "2009-02-01"') + self.assertEquals(len(rset), 2) # admin/anon but no ldap user since it doesn't support creation_date + def test_union(self): afeids = self.execute('State X') ueids = self.execute('EUser X') diff -r 536e421b082b -r 6a25c58a1c23 server/test/unittest_migractions.py --- a/server/test/unittest_migractions.py Mon Mar 02 20:44:14 2009 +0100 +++ b/server/test/unittest_migractions.py Mon Mar 02 21:03:54 2009 +0100 @@ -40,6 +40,8 @@ self.mh = ServerMigrationHelper(self.repo.config, migrschema, repo=self.repo, cnx=self.cnx, interactive=False) + assert self.cnx is self.mh._cnx + assert self.session is self.mh.session, (self.session.id, self.mh.session.id) def test_add_attribute_int(self): self.failIf('whatever' in self.schema) @@ -424,6 +426,16 @@ # why this commit is necessary is unclear to me (though without it # next test may fail complaining of missing tables self.commit() + + def test_set_state(self): + user = self.session.user + self.set_debug(True) + self.mh.set_state(user.eid, 'deactivated') + user.clear_related_cache('in_state', 'subject') + try: + self.assertEquals(user.state, 'deactivated') + finally: + self.set_debug(False) if __name__ == '__main__': unittest_main() diff -r 536e421b082b -r 6a25c58a1c23 server/test/unittest_rqlannotation.py --- a/server/test/unittest_rqlannotation.py Mon Mar 02 20:44:14 2009 +0100 +++ b/server/test/unittest_rqlannotation.py Mon Mar 02 21:03:54 2009 +0100 @@ -286,6 +286,12 @@ '(EXISTS(S owned_by 1)) OR (EXISTS(S documented_by N, N title "published"))') self.assertEquals(rqlst.defined_vars['S']._q_invariant, True) + def test_nonregr_ambiguity(self): + rqlst = self._prepare('Note N WHERE N attachment F') + # N may be an image as well, not invariant + self.assertEquals(rqlst.defined_vars['N']._q_invariant, False) + self.assertEquals(rqlst.defined_vars['F']._q_invariant, True) + if __name__ == '__main__': from logilab.common.testlib import unittest_main unittest_main() diff -r 536e421b082b -r 6a25c58a1c23 test/unittest_schema.py --- a/test/unittest_schema.py Mon Mar 02 20:44:14 2009 +0100 +++ b/test/unittest_schema.py Mon Mar 02 21:03:54 2009 +0100 @@ -226,6 +226,7 @@ def setUp(self): self.loader = CubicWebSchemaLoader() self.loader.defined = {} + self.loader.loaded_files = [] self.loader._instantiate_handlers() def _test(self, schemafile, msg): diff -r 536e421b082b -r 6a25c58a1c23 view.py --- a/view.py Mon Mar 02 20:44:14 2009 +0100 +++ b/view.py Mon Mar 02 21:03:54 2009 +0100 @@ -304,7 +304,22 @@ def create_url(self, etype, **kwargs): """ return the url of the entity creation form for a given entity type""" return self.req.build_url('add/%s'%etype, **kwargs) +<<<<<<< /home/syt/src/fcubicweb/cubicweb/view.py +======= + + +# concrete views base classes ################################################# + +class EntityView(View): + """base class for views applying on an entity (i.e. uniform result set) + """ + __registerer__ = accepts_registerer + __selectors__ = (accept,) + accepts = ('Any',) + category = 'entityview' + +>>>>>>> /tmp/view.py~other.mliJlS def field(self, label, value, row=True, show_label=True, w=None, tr=True): """ read-only field """ if w is None: diff -r 536e421b082b -r 6a25c58a1c23 web/data/cubicweb.ajax.js --- a/web/data/cubicweb.ajax.js Mon Mar 02 20:44:14 2009 +0100 +++ b/web/data/cubicweb.ajax.js Mon Mar 02 21:03:54 2009 +0100 @@ -35,6 +35,9 @@ if (typeof buildWidgets != 'undefined') { buildWidgets(node); } + if (typeof roundedCornersOnLoad != 'undefined') { + roundedCornersOnLoad(); + } } // cubicweb loadxhtml plugin to make jquery handle xhtml response diff -r 536e421b082b -r 6a25c58a1c23 web/data/cubicweb.edition.js --- a/web/data/cubicweb.edition.js Mon Mar 02 20:44:14 2009 +0100 +++ b/web/data/cubicweb.edition.js Mon Mar 02 21:03:54 2009 +0100 @@ -402,9 +402,8 @@ /* disable form buttons while the validation is being done */ function freezeFormButtons(formid) { - var formbuttons = jQuery(formid + ' input.validateButton'); jQuery('#progress').show(); - jQuery(formid + ' input.validateButton').attr('disabled', 'disabled'); + jQuery('#' + formid + ' input.validateButton').attr('disabled', 'disabled'); return true; } diff -r 536e421b082b -r 6a25c58a1c23 web/data/cubicweb.formfilter.js --- a/web/data/cubicweb.formfilter.js Mon Mar 02 20:44:14 2009 +0100 +++ b/web/data/cubicweb.formfilter.js Mon Mar 02 21:03:54 2009 +0100 @@ -105,14 +105,18 @@ var SELECTED_IMG = baseuri()+"data/black-check.png"; var UNSELECTED_IMG = baseuri()+"data/no-check-no-border.png"; -function initFacetBoxEvents(root){ +function initFacetBoxEvents(root) { + // facetargs : (divid, vid, paginate, extraargs) root = root || document; jQuery(root).find('form').each(function () { var form = jQuery(this); - var facetargs = evalJSON(form.attr('cubicweb:facetargs')); - if (facetargs !== undefined && facetargs.length) { + // NOTE: don't evaluate facetargs here but in callbacks since its value + // may changes and we must send its value when the callback is + // called, not when the page is initialized + var facetargs = form.attr('cubicweb:facetargs'); + if (facetargs !== undefined) { form.submit(function() { - buildRQL.apply(null, facetargs); //(divid, vid, paginate, extraargs); + buildRQL.apply(null, evalJSON(form.attr('cubicweb:facetargs'))); return false; }); form.find('div.facet').each(function() { @@ -144,13 +148,13 @@ jQuery(this).addClass('facetValueSelected'); jQuery(this).find('img').attr('src', SELECTED_IMG); } - buildRQL.apply(null, facetargs); // (divid, vid, paginate, extraargs); + buildRQL.apply(null, evalJSON(form.attr('cubicweb:facetargs'))); facet.find('.facetBody').animate({scrollTop: 0}, ''); }); facet.find('select.facetOperator').change(function() { var nbselected = facet.find('div.facetValueSelected').length; if (nbselected >= 2) { - buildRQL.apply(null, facetargs); // (divid, vid, paginate, extraargs); + buildRQL.apply(null, evalJSON(form.attr('cubicweb:facetargs'))); } }); facet.find('div.facetTitle').click(function() { @@ -169,8 +173,7 @@ root = root || document; jQuery(root).find('form').each(function () { var form = jQuery(this); - var facetargs = form.attr('cubicweb:facetargs'); - if (facetargs) { + if (form.attr('cubicweb:facetargs')) { form.find('div.facet').each(function() { var facet = jQuery(this); var lastSelected = null; diff -r 536e421b082b -r 6a25c58a1c23 web/data/cubicweb.widgets.js --- a/web/data/cubicweb.widgets.js Mon Mar 02 20:44:14 2009 +0100 +++ b/web/data/cubicweb.widgets.js Mon Mar 02 21:03:54 2009 +0100 @@ -181,6 +181,14 @@ } } +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 536e421b082b -r 6a25c58a1c23 web/facet.py --- a/web/facet.py Mon Mar 02 20:44:14 2009 +0100 +++ b/web/facet.py Mon Mar 02 21:03:54 2009 +0100 @@ -506,10 +506,7 @@ def _render(self): title = html_escape(self.facet.title) facetid = html_escape(self.facet.id) - if len(self.items) > 6: - self.w(u'
\n' % facetid) - else: - self.w(u'
\n' % facetid) + self.w(u'
\n' % facetid) self.w(u'
%s
\n' % (html_escape(facetid), title)) if self.facet.support_and(): @@ -522,6 +519,8 @@ cssclass = '' if not self.facet.start_unfolded: cssclass += ' hidden' + if len(self.items) > 6: + cssclass +=' overflowed' self.w(u'
\n' % cssclass) for item in self.items: item.render(self.w) diff -r 536e421b082b -r 6a25c58a1c23 web/test/unittest_views_baseforms.py --- a/web/test/unittest_views_baseforms.py Mon Mar 02 20:44:14 2009 +0100 +++ b/web/test/unittest_views_baseforms.py Mon Mar 02 21:03:54 2009 +0100 @@ -19,7 +19,7 @@ return DateTime(0000, 1, 1) widgets.today = widgets.now = _today -def teardown_module(options, result): +def teardown_module(options, results): widgets.today = orig_today widgets.now = orig_now diff -r 536e421b082b -r 6a25c58a1c23 web/views/baseforms.py --- a/web/views/baseforms.py Mon Mar 02 20:44:14 2009 +0100 +++ b/web/views/baseforms.py Mon Mar 02 21:03:54 2009 +0100 @@ -46,6 +46,7 @@ """ask for confirmation before real deletion""" req, w = self.req, self.w _ = req._ + req.add_css('cubicweb.form.css') req.add_js('cubicweb.edition.js') w(u'\n' % _('this action is not reversible!')) @@ -98,6 +99,7 @@ transition = self.req.eid_rset(self.req.form['treid']).get_entity(0, 0) dest = transition.destination() self.req.add_js('cubicweb.edition.js') + self.req.add_css('cubicweb.form.css') _ = self.req._ form = ChangeStateForm(self.req, entity=entity, redirect_path=self.redirectpath(entity)) @@ -479,7 +481,6 @@ def on_submit(self, entity): return u'return freezeFormButtons(\'%s\')' % (self.domid) - def submited_message(self): return self.req._('element edited') diff -r 536e421b082b -r 6a25c58a1c23 web/views/baseviews.py --- a/web/views/baseviews.py Mon Mar 02 20:44:14 2009 +0100 +++ b/web/views/baseviews.py Mon Mar 02 21:03:54 2009 +0100 @@ -11,6 +11,8 @@ :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ +#from __future__ import with_statement + __docformat__ = "restructuredtext en" from warnings import warn @@ -50,6 +52,21 @@ entities) """ id = 'final' + # record generated i18n catalog messages + _('%d years') + _('%d months') + _('%d weeks') + _('%d days') + _('%d hours') + _('%d minutes') + _('%d seconds') + _('%d years') + _('%d months') + _('%d weeks') + _('%d days') + _('%d hours') + _('%d minutes') + _('%d seconds') def cell_call(self, row, col, props=None, displaytime=False, format='text/html'): etype = self.rset.description[row][col] @@ -61,23 +78,26 @@ self.w(entity.printable_value(rtype, value, format=format)) return if etype in ('Time', 'Interval'): - _ = self.req._ # value is DateTimeDelta but we have no idea about what is the # reference date here, so we can only approximate years and months + if format == 'text/html': + space = ' ' + else: + space = ' ' if value.days > 730: # 2 years - self.w(_('%d years') % (value.days // 365)) + self.w(self.req.__('%%d%syears' % space) % (value.days // 365)) elif value.days > 60: # 2 months - self.w(_('%d months') % (value.days // 30)) + self.w(self.req.__('%%d%smonths' % space) % (value.days // 30)) elif value.days > 14: # 2 weeks - self.w(_('%d weeks') % (value.days // 7)) + self.w(self.req.__('%%d%sweeks' % space) % (value.days // 7)) elif value.days > 2: - self.w(_('%s days') % int(value.days)) + self.w(self.req.__('%%d%sdays' % space) % int(value.days)) elif value.hours > 2: - self.w(_('%s hours') % int(value.hours)) + self.w(self.req.__('%%d%shours' % space) % int(value.hours)) elif value.minutes >= 2: - self.w(_('%s minutes') % int(value.minutes)) + self.w(self.req.__('%%d%sminutes' % space) % int(value.minutes)) else: - self.w(_('%s seconds') % int(value.seconds)) + self.w(self.req.__('%%d%sseconds' % space) % int(value.seconds)) return self.wdata(printable_value(self.req, etype, value, props, displaytime=displaytime)) @@ -223,27 +243,43 @@ non-meta in a first step, meta in a second step """ if hasattr(self, 'get_side_boxes_defs'): - for label, rset in self.get_side_boxes_defs(entity): - if rset: + sideboxes = [(label, rset) for label, rset in self.get_side_boxes_defs(entity) + if rset] + if sideboxes: + self.w(u'') + for label, rset in sideboxes: + self.w(u'') + self.w(u'
') self.w(u'
') self.wview('sidebox', rset, title=label) self.w(u'
') + self.w(u'
') elif siderelations: + self.w(u'') + self.w(u'') + self.w(u'
') self.w(u'
') for relatedinfos in siderelations: # if not relatedinfos[0].meta: # continue self._render_related_entities(entity, *relatedinfos) self.w(u'
') - 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, row=self.row) - except NotImplementedError: - # much probably a context insensitive box, which only implements - # .call() and not cell_call() - box.dispatch(w=self.w) + self.w(u'
') + boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset, + row=self.row, view=self, + context='incontext')) + if boxes: + self.w(u'') + for box in boxes: + self.w(u'') + self.w(u'
') + try: + box.dispatch(w=self.w, row=self.row) + except NotImplementedError: + # much probably a context insensitive box, which only implements + # .call() and not cell_call() + box.dispatch(w=self.w) + self.w(u'
') def is_side_related(self, rschema, eschema): return rschema.meta and \ diff -r 536e421b082b -r 6a25c58a1c23 web/views/euser.py --- a/web/views/euser.py Mon Mar 02 20:44:14 2009 +0100 +++ b/web/views/euser.py Mon Mar 02 21:03:54 2009 +0100 @@ -87,14 +87,6 @@ self.w(u'\n') -class FoafUsableView(FoafView): - id = 'foaf_usable' - # XXX killme - title = None - def call(self): - self.cell_call(0, 0) - - class EditGroups(FormMixIn, EntityView): """displays a simple euser / egroups editable table""" diff -r 536e421b082b -r 6a25c58a1c23 web/views/idownloadable.py --- a/web/views/idownloadable.py Mon Mar 02 20:44:14 2009 +0100 +++ b/web/views/idownloadable.py Mon Mar 02 21:03:54 2009 +0100 @@ -17,6 +17,12 @@ _ = unicode +def is_image(entity): + mt = entity.download_content_type() + if not (mt and mt.startswith('image/')): + return 0 + return 1 + def download_box(w, entity, title=None, label=None): req = entity.req w(u'
') @@ -32,11 +38,14 @@ w(u'
') w(u'
\n
\n') - + class DownloadBox(EntityBoxTemplate): id = 'download_box' - __select__ = (one_line_rset() & implements(IDownloadable) & match_context_prop()) + # no download box for images + # XXX primary_view selector ? + __select__ = (one_line_rset() & implements(IDownloadable) & match_context_prop() & ~ score_entity(is_image) order = 10 + def cell_call(self, row, col, title=None, label=None, **kwargs): entity = self.entity(row, col) download_box(self.w, entity, title, label) @@ -87,7 +96,8 @@ class IDownloadablePrimaryView(baseviews.PrimaryView): __select__ = implements(IDownloadable) - #skip_attrs = ('eid', 'data',) # XXX + # XXX File/Image attributes but this is not specified in the IDownloadable interface + skip_attrs = baseviews.PrimaryView.skip_attrs + ('data', 'name') def render_entity_title(self, entity): self.w(u'

%s %s

' @@ -96,12 +106,12 @@ def render_entity_attributes(self, entity, siderelations): super(IDownloadablePrimaryView, self).render_entity_attributes(entity, siderelations) - self.wview('downloadlink', entity.rset, title=self.req._('download'), row=entity.row) self.w(u'
') contenttype = entity.download_content_type() if contenttype.startswith('image/'): self.wview('image', entity.rset, row=entity.row) else: + self.wview('downloadlink', entity.rset, title=self.req._('download'), row=entity.row) try: if ENGINE.has_input(contenttype): self.w(entity.printable_value('data')) @@ -130,12 +140,6 @@ (url, name, durl, self.req._('download'))) -def is_image(entity): - mt = entity.download_content_type() - if not (mt and mt.startswith('image/')): - return 0 - return 1 - class ImageView(baseviews.EntityView): id = 'image' __select__ = implements(IDownloadable) & score_entity(is_image) diff -r 536e421b082b -r 6a25c58a1c23 web/views/tabs.py --- a/web/views/tabs.py Mon Mar 02 20:44:14 2009 +0100 +++ b/web/views/tabs.py Mon Mar 02 21:03:54 2009 +0100 @@ -32,22 +32,15 @@ });""" % {'event': 'load_%s' % vid, 'vid': vid, 'reloadable' : str(reloadable).lower()}) - def lazyview(self, vid, rql=None, eid=None, rset=None, static=False, - reloadable=False, show_spinbox=True, w=None): + def lazyview(self, vid, eid=None, reloadable=False, show_spinbox=True, w=None): """a lazy version of wview first version only support lazy viewing for an entity at a time """ - assert rql or eid or rset or static, \ - 'lazyview wants at least : rql, or an eid, or an rset -- or call it with static=True' w = w or self.w self.req.add_js('cubicweb.lazy.js') urlparams = {'vid' : vid, 'mode' : 'html'} - if rql: - urlparams['rql'] = rql - elif eid: + if eid: urlparams['rql'] = uilib.rql_for_eid(eid) - elif rset: - urlparams['rql'] = rset.printable_rql() w(u'
' % ( vid, html_escape(self.build_url('json', **urlparams)))) if show_spinbox: @@ -71,12 +64,12 @@ return str('%s_active_tab' % self.config.appid) def active_tab(self, tabs, default): - cookies = self.req.get_cookie() + cookie = self.req.get_cookie() cookiename = self.cookie_name - activetab = cookies.get(cookiename) + activetab = cookie.get(cookiename) if activetab is None: - cookies[cookiename] = default - self.req.set_cookie(cookies, cookiename) + cookie[cookiename] = default + self.req.set_cookie(cookie, cookiename) tab = default else: tab = activetab.value @@ -102,7 +95,7 @@ active_tab = self.active_tab(tabs, default) # build the html structure w = self.w - w(u'
' % entity.eid) + w(u'
') w(u'
    ') for tab in tabs: w(u'
  • ') @@ -116,16 +109,15 @@ w(u'
') for tab in tabs: w(u'
' % tab) - self.lazyview(tab, eid=entity.eid) + self.lazyview(tab, entity.eid) w(u'
') # call the set_tab() JS function *after* each tab is generated # because the callback binding needs to be done before self.req.html_headers.add_onload(u""" - jQuery('#entity-tabs-%(eeid)s > ul').tabs( { selected: %(tabindex)s }); + jQuery('#entity-tabs > ul').tabs( { selected: %(tabindex)s }); set_tab('%(vid)s', '%(cookiename)s'); """ % {'tabindex' : tabs.index(active_tab), 'vid' : active_tab, - 'eeid' : entity.eid, 'cookiename' : self.cookie_name}) diff -r 536e421b082b -r 6a25c58a1c23 web/views/treeview.py --- a/web/views/treeview.py Mon Mar 02 20:44:14 2009 +0100 +++ b/web/views/treeview.py Mon Mar 02 21:03:54 2009 +0100 @@ -18,26 +18,29 @@ return str('treestate-%s' % treeid) +from cubicweb.web.views.baseviews import OneLineView + class TreeView(EntityView): id = 'treeview' itemvid = 'treeitemview' css_classes = 'treeview widget' title = _('tree view') - def call(self, subvid=None, treeid=None, initial_load=True): + def call(self, subvid=None): if subvid is None and 'subvid' in self.req.form: subvid = self.req.form.pop('subvid') # consume it if subvid is None: subvid = 'oneline' - if treeid is None and 'treeid' in self.req.form: - treeid = self.req.form.pop('treeid') - assert treeid is not None - if initial_load: - self.req.add_css('jquery.treeview.css') - self.req.add_js(('cubicweb.ajax.js', 'jquery.treeview.js')) - self.req.html_headers.add_onload(u""" -jQuery("#tree-%s").treeview({toggle: toggleTree, prerendered: true});""" % treeid) - self.w(u'
    ' % (treeid, self.css_classes)) + self.req.add_css('jquery.treeview.css') + self.req.add_js(('cubicweb.ajax.js', 'jquery.treeview.js', 'cubicweb.widgets.js')) + # XXX noautoload is a quick hack to avoid treeview to be rebuilt + # after a json query and avoid double toggling bugs. + # Need to find a way to do that cleanly. + if 'noautoload' in self.req.form: + self.w(u'
      ' % self.css_classes) + else: + self.w(u'
        ' + % self.css_classes) for rowidx in xrange(len(self.rset)): self.wview(self.itemvid, self.rset, row=rowidx, col=0, vid=subvid, parentvid=self.id, treeid=treeid) @@ -54,6 +57,8 @@ def call(self, subvid=None, treeid=None, initial_load=True): super(FileTreeView, self).call(treeid=treeid, subvid='filetree-oneline', initial_load=initial_load) + + class FileItemInnerView(EntityView): """inner view used by the TreeItemView instead of oneline view @@ -65,18 +70,17 @@ def cell_call(self, row, col): entity = self.entity(row, col) if ITree.is_implemented_by(entity.__class__) and not entity.is_leaf(): - self.w(u'
        %s
        \n' % entity.view('oneline')) + self.w(u'
        %s
        ' % entity.view('oneline')) else: # XXX define specific CSS classes according to mime types - self.w(u'
        %s
        \n' % entity.view('oneline')) + self.w(u'
        %s
        ' % entity.view('oneline')) class DefaultTreeViewItemView(EntityView): """default treeitem view for entities which don't implement ITree""" id = 'treeitemview' - - def cell_call(self, row, col, vid='oneline', parentvid='treeview', treeid=None): - assert treeid is not None + + def cell_call(self, row, col, vid='oneline', parentvid='treeview'): entity = self.entity(row, col) itemview = self.view(vid, self.rset, row=row, col=col) if row == len(self.rset) - 1: @@ -92,81 +96,32 @@ """ id = 'treeitemview' __select_ = implements(ITree) - - def open_state(self, eeid, treeid): - cookies = self.req.get_cookie() - treestate = cookies.get(treecookiename(treeid)) - if treestate: - return str(eeid) in treestate.value.split(';') - return False - - def cell_call(self, row, col, treeid, vid='oneline', parentvid='treeview'): - w = self.w + + def cell_call(self, row, col, vid='oneline', parentvid='treeview'): entity = self.entity(row, col) - liclasses = [] + cssclasses = [] is_leaf = False - is_last = row == len(self.rset) - 1 - is_open = self.open_state(entity.eid, treeid) + if row == len(self.rset) - 1: + is_leaf = True if not hasattr(entity, 'is_leaf') or entity.is_leaf(): - if is_last: - liclasses.append('last') - w(u'
      • ' % u' '.join(liclasses)) + if is_leaf : cssclasses.append('last') + self.w(u'
      • ' % u' '.join(cssclasses)) else: rql = entity.children_rql() % {'x': entity.eid} url = html_escape(self.build_url('json', rql=rql, vid=parentvid, pageid=self.req.pageid, - treeid=treeid, - subvid=vid)) - divclasses = ['hitarea'] - if is_open: - liclasses.append('collapsable') - divclasses.append('collapsable-hitarea') - else: - liclasses.append('expandable') - divclasses.append('closed-hitarea expandable-hitarea') - if is_last: - if is_open: - liclasses.append('lastCollapsable') - divclasses.append('lastCollapsable-hitarea') - else: - liclasses.append('lastExpandable') - divclasses.append('lastExpandable-hitarea') - if is_open: - w(u'
      • ' % u' '.join(liclasses)) - else: - w(u'
      • ' % (url, u' '.join(liclasses))) - if is_leaf: - divtail = '' - else: - divtail = ''' onclick="async_remote_exec('node_clicked', '%s', '%s')"''' % \ - (treeid, entity.eid) - w(u'
        ' % (u' '.join(divclasses), divtail)) - + subvid=vid, + noautoload=True)) + cssclasses.append('expandable') + divclasses = ['hitarea expandable-hitarea'] + if is_leaf : + cssclasses.append('lastExpandable') + divclasses.append('lastExpandable-hitarea') + self.w(u'
      • ' % (url, u' '.join(cssclasses))) + self.w(u'
        ' % u' '.join(divclasses)) # add empty
          because jquery's treeview plugin checks for # sublists presence - if not is_open: - w(u'
          • place holder
          ') - # the local node info + self.w(u'
          • place holder
          ') self.wview(vid, self.rset, row=row, col=col) - if is_open: # recurse if needed - self.wview(parentvid, self.req.execute(rql), treeid=treeid, initial_load=False) - w(u'') - + self.w(u'') -@monkeypatch(JSonController) -def js_node_clicked(self, treeid, nodeeid): - """add/remove eid in treestate cookie""" - cookies = self.req.get_cookie() - statename = treecookiename(treeid) - treestate = cookies.get(statename) - if treestate is None: - cookies[statename] = nodeeid - self.req.set_cookie(cookies, statename) - else: - marked = set(filter(None, treestate.value.split(';'))) - if nodeeid in marked: - marked.remove(nodeeid) - else: - marked.add(nodeeid) - cookies[statename] = ';'.join(marked) - self.req.set_cookie(cookies, statename)