--- a/devtools/repotest.py Sat Dec 18 23:12:14 2010 +0100
+++ b/devtools/repotest.py Tue Jan 04 14:11:54 2011 +0100
@@ -141,7 +141,7 @@
from rql import RQLHelper
from cubicweb.devtools.fake import FakeRepo, FakeSession
-from cubicweb.server import set_debug
+from cubicweb.server import set_debug, debugged
from cubicweb.server.querier import QuerierHelper
from cubicweb.server.session import Session
from cubicweb.server.sources.rql2sql import SQLGenerator, remove_unused_solutions
@@ -171,6 +171,8 @@
def set_debug(self, debug):
set_debug(debug)
+ def debugged(self, debug):
+ return debugged(debug)
def _prepare(self, rql):
#print '******************** prepare', rql
@@ -222,6 +224,8 @@
def set_debug(self, debug):
set_debug(debug)
+ def debugged(self, debug):
+ return debugged(debug)
def _rqlhelper(self):
rqlhelper = self.repo.vreg.rqlhelper
--- a/doc/book/en/devrepo/repo/hooks.rst Sat Dec 18 23:12:14 2010 +0100
+++ b/doc/book/en/devrepo/repo/hooks.rst Tue Jan 04 14:11:54 2011 +0100
@@ -97,7 +97,7 @@
.. sourcecode:: python
- from cubicweb.server.hook import Hook, Operation, match_rtype
+ from cubicweb.server.hook import Hook, DataOperationMixIn, Operation, match_rtype
def check_cycle(self, session, eid, rtype, role='subject'):
parents = set([eid])
@@ -110,7 +110,7 @@
parents.add(parent.eid)
- class CheckSubsidiaryCycleOp(Operation):
+ class CheckSubsidiaryCycleOp(DataOperationMixIn, Operation):
def precommit_event(self):
check_cycle(self.session, self.eidto, 'subsidiary_of')
@@ -131,7 +131,7 @@
In the above example, our hook will instantiate an operation each time the hook
is called, i.e. each time the `subsidiary_of` relation is set. There is an
alternative method to schedule an operation from a hook, using the
-:func:`set_operation` function.
+:func:`get_instance` class method.
.. sourcecode:: python
@@ -143,13 +143,12 @@
__select__ = Hook.__select__ & match_rtype('subsidiary_of')
def __call__(self):
- set_operation(self._cw, 'subsidiary_cycle_detection', self.eidto,
- CheckSubsidiaryCycleOp)
+ CheckSubsidiaryCycleOp.get_instance(self._cw).add_data(self.eidto)
class CheckSubsidiaryCycleOp(Operation):
def precommit_event(self):
- for eid in self.session.transaction_data['subsidiary_cycle_detection']:
+ for eid in self.get_data():
check_cycle(self.session, eid, self.rtype)
@@ -169,30 +168,31 @@
Reminder
~~~~~~~~
-Never, ever use the `entity.foo = 42` notation to update an entity. It will not
-work.To updating an entity attribute or relation, uses :meth:`set_attributes` and
+You should never use the `entity.foo = 42` notation to update an
+entity. It will not do what you expect (updating the
+database). Instead, use the :meth:`set_attributes` and
:meth:`set_relations` methods.
How to choose between a before and an after event ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-'before_*' hooks give you access to the old attribute (or relation)
-values. You can also hi-jack actually edited stuff in the case of entity
-modification. Needing one of this will definitly guide your choice.
+`before_*` hooks give you access to the old attribute (or relation)
+values. You can also intercept and update edited values in the case of
+entity modification before they reach the database.
Else the question is: should I need to do things before or after the actual
-modification. If the answer is "it doesn't matter", use an 'after' event.
+modification ? If the answer is "it doesn't matter", use an 'after' event.
Validation Errors
~~~~~~~~~~~~~~~~~
-When a hook is responsible to maintain the consistency of the data model detect
-an error, it must use a specific exception named
+When a hook which is responsible to maintain the consistency of the
+data model detects an error, it must use a specific exception named
:exc:`~cubicweb.ValidationError`. Raising anything but a (subclass of)
-:exc:`~cubicweb.ValidationError` is a programming error. Raising a it entails
-aborting the current transaction.
+:exc:`~cubicweb.ValidationError` is a programming error. Raising it
+entails aborting the current transaction.
This exception is used to convey enough information up to the user
interface. Hence its constructor is different from the default Exception
@@ -204,6 +204,11 @@
an end-user facing message (hence properly translated) relating the
problem.
+.. sourcecode:: python
+
+ raise ValidationError(earth.eid, {'sea_level': self._cw._('too high'),
+ 'temperature': self._cw._('too hot')})
+
Checking for object created/deleted in the current transaction
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -228,7 +233,8 @@
Relations which are defined in the schema as `inlined` (see :ref:`RelationType`
for details) are inserted in the database at the same time as entity attributes.
-This may have some side effect, for instance when creating entity and setting an
-inlined relation in the same rql query, when 'before_add_relation' for that
-relation will be run, the relation will already exist in the database (it's
-usually not the case).
+
+This may have some side effect, for instance when creating an entity
+and setting an inlined relation in the same rql query, then at
+`before_add_relation` time, the relation will already exist in the
+database (it is otherwise not the case).
--- a/entities/adapters.py Sat Dec 18 23:12:14 2010 +0100
+++ b/entities/adapters.py Tue Jan 04 14:11:54 2011 +0100
@@ -484,5 +484,5 @@
def raise_user_exception(self):
etype, rtypes = self.exc.args
msg = self._cw._('violates unique_together constraints (%s)') % (
- ', '.join(self._cw._(rtypes)))
+ ', '.join([self._cw._(rtype) for rtype in rtypes]))
raise ValidationError(self.entity.eid, dict((col, msg) for col in rtypes))
--- a/hooks/workflow.py Sat Dec 18 23:12:14 2010 +0100
+++ b/hooks/workflow.py Tue Jan 04 14:11:54 2011 +0100
@@ -191,6 +191,8 @@
msg = session._('mandatory relation')
raise ValidationError(entity.eid, {qname: msg})
forentity = session.entity_from_eid(foreid)
+ # see comment in the TrInfo entity definition
+ entity.cw_edited['tr_count']=len(forentity.reverse_wf_info_for)
iworkflowable = forentity.cw_adapt_to('IWorkflowable')
# then check it has a workflow set, unless we're in the process of changing
# entity's workflow
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.10.7_Any.py Tue Jan 04 14:11:54 2011 +0100
@@ -0,0 +1,2 @@
+add_attribute('TrInfo', 'tr_count')
+sync_schema_props_perms('TrInfo')
--- a/schemas/workflow.py Sat Dec 18 23:12:14 2010 +0100
+++ b/schemas/workflow.py Tue Jan 04 14:11:54 2011 +0100
@@ -22,7 +22,7 @@
_ = unicode
from yams.buildobjs import (EntityType, RelationType, SubjectRelation,
- RichString, String)
+ RichString, String, Int)
from cubicweb.schema import RQLConstraint, RQLUniqueConstraint
from cubicweb.schemas import (META_ETYPE_PERMS, META_RTYPE_PERMS,
HOOKS_RTYPE_PERMS)
@@ -159,13 +159,21 @@
'delete': (), # XXX should we allow managers to delete TrInfo?
'update': ('managers', 'owners',),
}
-
- from_state = SubjectRelation('State', cardinality='1*')
- to_state = SubjectRelation('State', cardinality='1*')
+ # The unique_together constraint ensures that 2 repositories
+ # sharing the db won't be able to fire a transition simultaneously
+ # on the same entity tr_count is filled in the FireTransitionHook
+ # to the number of TrInfo attached to the entity on which we
+ # attempt to fire a transition. In other word, it contains the
+ # rank of the TrInfo for that entity, and the constraint says we
+ # cannot have 2 TrInfo with the same rank.
+ __unique_together__ = [('tr_count', 'wf_info_for')]
+ from_state = SubjectRelation('State', cardinality='1*', inlined=True)
+ to_state = SubjectRelation('State', cardinality='1*', inlined=True)
# make by_transition optional because we want to allow managers to set
# entity into an arbitrary state without having to respect wf transition
by_transition = SubjectRelation('BaseTransition', cardinality='?*')
comment = RichString(fulltextindexed=True)
+ tr_count = Int(description='autocomputed attribute used to ensure transition coherency')
# get actor and date time using owned_by and creation_date
class from_state(RelationType):
--- a/server/hook.py Sat Dec 18 23:12:14 2010 +0100
+++ b/server/hook.py Tue Jan 04 14:11:54 2011 +0100
@@ -105,17 +105,17 @@
When called for one of these events, hook will have an `entity` attribute
containing the entity instance.
-* 'before_add_entity', 'before_update_entity':
+* `before_add_entity`, `before_update_entity`:
- on those events, you can check what attributes of the entity are modified in
`entity.cw_edited` (by definition the database is not yet updated in a before
event)
- - you are allowed to further modify the entity before database operations,
- using the dictionary notation. By doing this, you'll avoid the need for a
- whole new rql query processing, the only difference is that the underlying
- backend query (eg usually sql) will contains the additional data. For
- example:
+ - you are allowed to further modify the entity before database
+ operations, using the dictionary notation on `cw_edited`. By doing
+ this, you'll avoid the need for a whole new rql query processing,
+ the only difference is that the underlying backend query (eg
+ usually sql) will contains the additional data. For example:
.. sourcecode:: python
@@ -136,17 +136,17 @@
Similarly, removing an attribute from `cw_edited` will cancel its
modification.
- - on 'before_update_entity' event, you can access to old and new values in
+ - on `before_update_entity` event, you can access to old and new values in
this hook, by using `entity.cw_edited.oldnewvalue(attr)`
-* 'after_add_entity', 'after_update_entity'
+* `after_add_entity`, `after_update_entity`
- on those events, you can still check what attributes of the entity are
modified in `entity.cw_edited` but you can't get anymore the old value, nor
modify it.
-* 'before_delete_entity', 'after_delete_entity'
+* `before_delete_entity`, `after_delete_entity`
- on those events, the entity has no `cw_edited` set.
@@ -158,11 +158,11 @@
attributes containing respectivly the eid of the subject entity, the relation
type and the eid of the object entity.
-* 'before_add_relation', 'before_delete_relation'
+* `before_add_relation`, `before_delete_relation`
- on those events, you can still get original relation by issuing a rql query
-* 'after_add_relation', 'after_delete_relation'
+* `after_add_relation`, `after_delete_relation`
This is an occasion to remind us that relations support the add / delete
operation, but no update.
@@ -171,8 +171,8 @@
Non data events
~~~~~~~~~~~~~~~
-Hooks called on server start/maintenance/stop event (eg 'server_startup',
-'server_maintenance', 'server_shutdown') have a `repo` attribute, but *their
+Hooks called on server start/maintenance/stop event (eg `server_startup`,
+`server_maintenance`, `server_shutdown`) have a `repo` attribute, but *their
`_cw` attribute is None*. The `server_startup` is called on regular startup,
while `server_maintenance` is called on cubicweb-ctl upgrade or shell
commands. `server_shutdown` is called anyway.
@@ -180,7 +180,7 @@
Hooks called on backup/restore event (eg 'server_backup', 'server_restore') have
a `repo` and a `timestamp` attributes, but *their `_cw` attribute is None*.
-Hooks called on session event (eg 'session_open', 'session_close') have no
+Hooks called on session event (eg `session_open`, `session_close`) have no
special attribute.
@@ -400,7 +400,7 @@
.. Note::
- Do not forget to extend the base class selectors as in ::
+ Do not forget to extend the base class selectors as in:
.. sourcecode:: python
@@ -609,7 +609,7 @@
An operation is triggered on connections pool events related to
commit / rollback transations. Possible events are:
- * 'precommit':
+ * `precommit`:
the transaction is being prepared for commit. You can freely do any heavy
computation, raise an exception if the commit can't go. or even add some
@@ -618,13 +618,13 @@
instance), you'll have to support the 'revertprecommit' event to revert
things by yourself
- * 'revertprecommit':
+ * `revertprecommit`:
if an operation failed while being pre-commited, this event is triggered
for all operations which had their 'precommit' event already fired to let
them revert things (including the operation which made the commit fail)
- * 'rollback':
+ * `rollback`:
the transaction has been either rollbacked either:
@@ -632,7 +632,7 @@
* a 'precommit' event failed, in which case all operations are rollbacked
once 'revertprecommit'' has been called
- * 'postcommit':
+ * `postcommit`:
the transaction is over. All the ORM entities accessed by the earlier
transaction are invalid. If you need to work on the database, you need to
@@ -642,7 +642,7 @@
For an operation to support an event, one has to implement the `<event
name>_event` method with no arguments.
- Notice order of operations may be important, and is controlled according to
+ The order of operations may be important, and is controlled according to
the insert_index's method output (whose implementation vary according to the
base hook class used).
"""
@@ -736,6 +736,7 @@
`value`, since handling operations becomes costly on massive data import.
Usage looks like:
+
.. sourcecode:: python
class MyEntityHook(Hook):
--- a/server/msplanner.py Sat Dec 18 23:12:14 2010 +0100
+++ b/server/msplanner.py Tue Jan 04 14:11:54 2011 +0100
@@ -856,6 +856,25 @@
needsel = set()
if not self._sourcesterms:
terms += scope.defined_vars.values() + scope.aliases.values()
+ if isinstance(term, Relation) and len(sources) > 1:
+ variants = set()
+ partterms = [term]
+ for vref in term.get_nodes(VariableRef):
+ if not vref.variable._q_invariant:
+ variants.add(vref.name)
+ if len(variants) == 2:
+ # we need an extra-step to fetch relations from each source
+ # before a join with prefetched inputs
+ # (see test_crossed_relation_noeid_needattr in
+ # unittest_msplanner / unittest_multisources)
+ needsel2 = needsel.copy()
+ needsel2.update(variants)
+ lhs, rhs = term.get_variable_parts()
+ steps.append( (sources, [term, getattr(lhs, 'variable', lhs),
+ getattr(rhs, 'variable', rhs)],
+ solindices, scope,
+ needsel2, False) )
+ sources = [self.system_source]
final = True
else:
# suppose this is a final step until the contrary is proven
@@ -1310,8 +1329,8 @@
# in this case we have to merge input maps before call to
# filter so already processed restriction are correctly
# removed
- solsinputmaps = ppi.merge_input_maps(solindices,
- complete=not (final and multifinal))
+ solsinputmaps = ppi.merge_input_maps(
+ solindices, complete=not (final and multifinal))
for solindices, inputmap in solsinputmaps:
minrqlst, insertedvars = vfilter.filter(
sources, terms, scope, set(solindices), needsel, final)
@@ -1328,7 +1347,8 @@
minrqlst, insertedvars = vfilter.filter(
sources, terms, scope, solindices, needsel, final)
if final:
- solsinputmaps = ppi.merge_input_maps(solindices)
+ solsinputmaps = ppi.merge_input_maps(
+ solindices, complete=not (final and multifinal))
if len(solsinputmaps) > 1:
refrqlst = minrqlst
for solindices, inputmap in solsinputmaps:
@@ -1548,7 +1568,7 @@
def visit_relation(self, node, newroot, terms):
if not node.is_types_restriction():
- if node in self.skip and self.solindices.issubset(self.skip[node]):
+ if not node in terms and node in self.skip and self.solindices.issubset(self.skip[node]):
if not self.schema.rschema(node.r_type).final:
# can't really skip the relation if one variable is selected
# and only referenced by this relation
--- a/server/test/data/extern_mapping.py Sat Dec 18 23:12:14 2010 +0100
+++ b/server/test/data/extern_mapping.py Tue Jan 04 14:11:54 2011 +0100
@@ -15,8 +15,9 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-"""
+"""mapping file for source used in unittest_multisources.py"""
-"""
support_entities = {'Card': True, 'Affaire': True, 'State': True}
support_relations = {'in_state': True, 'documented_by': True, 'multisource_inlined_rel': True}
+
+cross_relations = set( ('documented_by',) )
--- a/server/test/unittest_msplanner.py Sat Dec 18 23:12:14 2010 +0100
+++ b/server/test/unittest_msplanner.py Tue Jan 04 14:11:54 2011 +0100
@@ -18,6 +18,7 @@
from logilab.common.decorators import clear_cache
+from yams.buildobjs import RelationDefinition
from rql import BadRQLQuery
from cubicweb.devtools import init_test_database
@@ -1607,20 +1608,84 @@
('FetchStep', [('Any Y,T WHERE Y type T, Y is Note', [{'T': 'String', 'Y': 'Note'}])],
[self.cards, self.system], None,
{'T': 'table1.C1', 'Y': 'table1.C0', 'Y.type': 'table1.C1'}, []),
- ('UnionStep', None, None,
- [('OneFetchStep', [('Any X,Y,T WHERE X multisource_crossed_rel Y, Y type T, X type T, X is Note, Y is Note',
- [{'T': 'String', 'X': 'Note', 'Y': 'Note'}])],
- None, None, [self.cards], None,
- []),
- ('OneFetchStep', [('Any X,Y,T WHERE X multisource_crossed_rel Y, Y type T, X type T, X is Note, Y is Note',
- [{'T': 'String', 'X': 'Note', 'Y': 'Note'}])],
- None, None, [self.system],
- {'T': 'table1.C1', 'X': 'table0.C0', 'X.type': 'table0.C1',
- 'Y': 'table1.C0', 'Y.type': 'table1.C1'},
- [])]
- )],
+ ('FetchStep', [('Any X,Y WHERE X multisource_crossed_rel Y, X is Note, Y is Note',
+ [{'X': 'Note', 'Y': 'Note'}])],
+ [self.cards, self.system], None,
+ {'X': 'table2.C0', 'Y': 'table2.C1'},
+ []),
+ ('OneFetchStep', [('Any X,Y,T WHERE X multisource_crossed_rel Y, Y type T, X type T, '
+ 'X is Note, Y is Note, Y identity A, X identity B, A is Note, B is Note',
+ [{u'A': 'Note', u'B': 'Note', 'T': 'String', 'X': 'Note', 'Y': 'Note'}])],
+ None, None,
+ [self.system],
+ {'A': 'table1.C0',
+ 'B': 'table0.C0',
+ 'T': 'table1.C1',
+ 'X': 'table2.C0',
+ 'X.type': 'table0.C1',
+ 'Y': 'table2.C1',
+ 'Y.type': 'table1.C1'},
+ []),
+ ],
{'x': 999999,})
+ def test_crossed_relation_noeid_needattr(self):
+ # http://www.cubicweb.org/ticket/1382452
+ self._test('DISTINCT Any DEP WHERE DEP is Note, P type "cubicweb-foo", P multisource_crossed_rel DEP, DEP type LIKE "cubicweb%"',
+ [('FetchStep', [(u'Any DEP WHERE DEP type LIKE "cubicweb%", DEP is Note',
+ [{'DEP': 'Note'}])],
+ [self.cards, self.system], None,
+ {'DEP': 'table0.C0'},
+ []),
+ ('FetchStep', [(u'Any P WHERE P type "cubicweb-foo", P is Note', [{'P': 'Note'}])],
+ [self.cards, self.system], None, {'P': 'table1.C0'},
+ []),
+ ('FetchStep', [('Any DEP,P WHERE P multisource_crossed_rel DEP, DEP is Note, P is Note',
+ [{'DEP': 'Note', 'P': 'Note'}])],
+ [self.cards, self.system], None, {'DEP': 'table2.C0', 'P': 'table2.C1'},
+ []),
+ ('OneFetchStep',
+ [('DISTINCT Any DEP WHERE P multisource_crossed_rel DEP, DEP is Note, '
+ 'P is Note, DEP identity A, P identity B, A is Note, B is Note',
+ [{u'A': 'Note', u'B': 'Note', 'DEP': 'Note', 'P': 'Note'}])],
+ None, None, [self.system],
+ {'A': 'table0.C0', 'B': 'table1.C0', 'DEP': 'table2.C0', 'P': 'table2.C1'},
+ [])])
+
+ def test_crossed_relation_noeid_invariant(self):
+ # see comment in http://www.cubicweb.org/ticket/1382452
+ self.schema.add_relation_def(
+ RelationDefinition(subject='Note', name='multisource_crossed_rel', object='Affaire'))
+ self.repo.set_schema(self.schema)
+ try:
+ self._test('DISTINCT Any P,DEP WHERE P type "cubicweb-foo", P multisource_crossed_rel DEP',
+ [('FetchStep',
+ [('Any DEP WHERE DEP is Note', [{'DEP': 'Note'}])],
+ [self.cards, self.system], None, {'DEP': 'table0.C0'}, []),
+ ('FetchStep',
+ [(u'Any P WHERE P type "cubicweb-foo", P is Note', [{'P': 'Note'}])],
+ [self.cards, self.system], None, {'P': 'table1.C0'}, []),
+ ('UnionStep', None, None,
+ [('OneFetchStep',
+ [('DISTINCT Any P,DEP WHERE P multisource_crossed_rel DEP, DEP is Note, P is Note',
+ [{'DEP': 'Note', 'P': 'Note'}])],
+ None, None, [self.cards], None, []),
+ ('OneFetchStep',
+ [('DISTINCT Any P,DEP WHERE P multisource_crossed_rel DEP, DEP is Note, P is Note',
+ [{'DEP': 'Note', 'P': 'Note'}])],
+ None, None, [self.system],
+ {'DEP': 'table0.C0', 'P': 'table1.C0'},
+ []),
+ ('OneFetchStep',
+ [('DISTINCT Any P,DEP WHERE P multisource_crossed_rel DEP, DEP is Affaire, P is Note',
+ [{'DEP': 'Affaire', 'P': 'Note'}])],
+ None, None, [self.system], {'P': 'table1.C0'},
+ [])])
+ ])
+ finally:
+ self.schema.del_relation_def('Note', 'multisource_crossed_rel', 'Affaire')
+ self.repo.set_schema(self.schema)
+
# edition queries tests ###################################################
def test_insert_simplified_var_1(self):
--- a/server/test/unittest_multisources.py Sat Dec 18 23:12:14 2010 +0100
+++ b/server/test/unittest_multisources.py Tue Jan 04 14:11:54 2011 +0100
@@ -86,7 +86,9 @@
TestServerConfiguration.no_sqlite_wrap = False
class TwoSourcesTC(CubicWebTC):
-
+ """Main repo -> extern-multi -> extern
+ \-------------/
+ """
@classmethod
def _refresh_repo(cls):
super(TwoSourcesTC, cls)._refresh_repo()
@@ -333,6 +335,19 @@
cu.execute('DELETE Card X WHERE X eid %(x)s', {'x':ceid})
cnx3.commit()
+ def test_crossed_relation_noeid_needattr(self):
+ """http://www.cubicweb.org/ticket/1382452"""
+ aff1 = self.sexecute('INSERT Affaire X: X ref "AFFREF"')[0][0]
+ # link within extern source
+ ec1 = self.sexecute('Card X WHERE X wikiid "zzz"')[0][0]
+ self.sexecute('SET A documented_by C WHERE E eid %(a)s, C eid %(c)s',
+ {'a': aff1, 'c': ec1})
+ # link from system to extern source
+ self.sexecute('SET A documented_by C WHERE E eid %(a)s, C eid %(c)s',
+ {'a': aff1, 'c': self.ic2})
+ rset = self.sexecute('DISTINCT Any DEP WHERE P ref "AFFREF", P documented_by DEP, DEP wikiid LIKE "z%"')
+ self.assertEqual(sorted(rset.rows), [[ec1], [self.ic2]])
+
def test_nonregr1(self):
ueid = self.session.user.eid
affaire = self.sexecute('Affaire X WHERE X ref "AFFREF"').get_entity(0, 0)
--- a/view.py Sat Dec 18 23:12:14 2010 +0100
+++ b/view.py Tue Jan 04 14:11:54 2011 +0100
@@ -40,6 +40,11 @@
NOINDEX = u'<meta name="ROBOTS" content="NOINDEX" />'
NOFOLLOW = u'<meta name="ROBOTS" content="NOFOLLOW" />'
+
+CW_XMLNS = '''[
+ <!ATTLIST html xmlns:cubicweb CDATA #FIXED \'http://www.logilab.org/2008/cubicweb\' >
+ ]
+'''
CW_XHTML_EXTENSIONS = '''[
<!ATTLIST html xmlns:cubicweb CDATA #FIXED \'http://www.logilab.org/2008/cubicweb\' >
@@ -81,9 +86,9 @@
"> ] '''
TRANSITIONAL_DOCTYPE = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" %s>\n' % CW_XHTML_EXTENSIONS
-TRANSITIONAL_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n'
+TRANSITIONAL_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" %s>\n' % CW_XMLNS
STRICT_DOCTYPE = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" %s>\n' % CW_XHTML_EXTENSIONS
-STRICT_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
+STRICT_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" %s>\n' % CW_XMLNS
# base view object ############################################################
--- a/web/views/reledit.py Sat Dec 18 23:12:14 2010 +0100
+++ b/web/views/reledit.py Tue Jan 04 14:11:54 2011 +0100
@@ -78,7 +78,7 @@
assert rtype
assert role in ('subject', 'object'), '%s is not an acceptable role value' % role
self._cw.add_css('cubicweb.form.css')
- self._cw.add_js('cubicweb.reledit.js', 'cubicweb.edition.js')
+ self._cw.add_js(('cubicweb.reledit.js', 'cubicweb.edition.js', 'cubicweb.ajax.js'))
entity = self.cw_rset.get_entity(row, col)
rschema = self._cw.vreg.schema[rtype]
self._rules = rctrl.etype_get(entity.e_schema.type, rschema.type, role, '*')