On 2/9/12 6:07 AM, Ramon Navarro Bosch wrote:
> Hi Jonathan,
>
> It is ok for us, we saw on devel list you are going to come to europe
> on March, we would like to work on that before. On the other hand, we
> are trying to push a sprint about p.a.m. UI at PloneKonf.
>
> What are your plans, do you want us to begin to work on it before you
> come, or wait for you and work together?
>
> Martin, and David, it would be interesting to arrange a virtual
> meeting with you and Victor in order to talk about this subject and
> try to reach a possible technical solution friendly with dexterity
> design. Please let us know when we can talk.
>
> Ramon
>
> Enviat des de l'ipad!
>
> On 09/02/2012, at 0:34, Lewis Jonathan<jonatha...@mac.com> wrote:
>
>> Dear Ramon and everyone,
>>
>> Apologies again for the silence.
>>
>> Thinking about what our site needs, language independent fields are the top priority.
I took a look at LinguaPlone to understand how it handles
language-independent fields.
Its approach is basically:
1. Widgets for language independent fields are rendered in edit mode
when editing the canonical translation, but in view mode when editing
other translations.
2. The mutator for language-independent fields is wrapped such that when
the field is set, the new value is stored on each of the translations.
There are a few directions this could go for Dexterity. All of them
assume we have a way to mark which fields are language independent, so
let me describe that first. In Dexterity the way that we add metadata to
a schema is by setting "tagged values" on the schema interface. The
language independent flag can be handled using a tagged value which is a
dictionary indicating which fields are language independent. Like this:
from plone.directives import form
from zope import schema
class IEventInfo(form.Schema):
start = schema.DateTime(title='Start')
end = schema.DateTime(title='End')
IEventInfo.setTaggedValue('language_independent', {'start': True, 'end':
True})
And then code which needs to see if the field is language-independent
can do something like:
is_independent =
field.interface.queryTaggedValue('language_independent',
{}).get(field.__name__, False)
This is the same way that Dexterity's other metadata (widget hints,
primary field flag, etc.) are handled internally, but you may not have
encountered tagged values directly because we provide some more
user-friendly ways to set them:
1. grok directives (i.e. language_independent('start') within the schema
definition)
2. supermodel XML (i.e. lingua:independent="true" in an XML version of
the model)
3. through-the-web UI (which modifies the tagged value in memory and
then serializes the result to supermodel XML stored in the FTI)
To figure out how to handle all of these I'd recommend looking at how
the "primary" flag is set up. The supermodel import/export handler is in
plone.rfc822.supermodel. The grok directive is in
plone.directives.form.schema, down at the bottom.
Okay, with that implemented we can check if a field is
language-independent. Now we need to decide how to keep the field's
values in sync between translations.
I can see the start of a number of options...
1. Use an ObjectModified event handler. In it, determine which fields
changed that are designated language independent. If any, look up all
the other translations and write the values to those translations too.
But this could get complex because it's hard to guarantee the order of
execution vs. other ObjectModified handlers, and if you want to raise
ObjectModified on the other translations too then you need a way to
avoid endless recursion...
2. Use a custom z3c.form data manager. The data manager is the component
responsible for getting/setting form values from and to the form's
context. The default implementation simply adapts the context to the
field's interface and then uses simple attribute get/set. But a custom
implementation for multilingual content could do this for all
translations, or could make all the translations get/set
language-independent fields from the canonical translation. The main
problem with this approach is that it would only be a solution for
z3c.form...just trying to access the field as an attribute of a
Dexterity item does not use the data manager.
3. Write a language-independent-field-aware property descriptor like:
class LanguageIndependentProperty(object):
def __init__(self, field):
self.field = field
def __get__(self, inst, type=None):
# If inst is the canonical translation, get the value from the
instance dict;
# else find the canonical translation and get it from there
def __set__(self, inst, value):
# Find the canonical translation and set the value there
This could be used in behavior adapters, or in custom subclasses of
plone.dexterity.content.Item. The downside of this option is that either
you need to put your language-independent fields in behaviors, or you
need to create a custom class for your content type. (Since Dexterity
doesn't auto-generate accessors and mutators like Archetypes did, and
assumes values of the main schema can be reached via simple attribute
access on the content item.)
4. Create a generic MultiLingualItem subclass of Item and override
__getattribute__ and __setattr__ to Do The Right Thing for
language-independent fields.
Sorry I don't have a more conclusive path to offer you, but hopefully
this at least gets people brainstorming. There could well be a more
elegant option I haven't thought of yet as this is all rather off-the-cuff.
cheers,
David
As I'm going to participate in the PloneKonf sprint, I thought I'd chip
in to get more clarity, and perhaps get a conversation started :)
I also added this thread to the PloneKonf sprint page on coactivate:
http://www.coactivate.org/projects/plone-konferenz-sprint-2012/project-home
See my inline responses below:
On Sun, 2012-02-12 at 22:07 -0800, David Glick wrote:
<snip>
So these would be sub-tasks? Creating a grok directive, supermodel XML
parsing and TTW UI handling.
They're however not crucial, because in the meantime one could just call
setTaggedValue.
> Okay, with that implemented we can check if a field is
> language-independent. Now we need to decide how to keep the field's
> values in sync between translations.
>
> I can see the start of a number of options...
> 1. Use an ObjectModified event handler. In it, determine which fields
> changed that are designated language independent. If any, look up all
> the other translations and write the values to those translations too.
> But this could get complex because it's hard to guarantee the order of
> execution vs. other ObjectModified handlers, and if you want to raise
> ObjectModified on the other translations too then you need a way to
> avoid endless recursion...
-1
Recursion doesn't sound like too much of a problem, since AFAIK it's
possible to set a value without firing the ObjectModified event again.
Variable order of execution sounds like a problem though.
> 2. Use a custom z3c.form data manager. The data manager is the component
> responsible for getting/setting form values from and to the form's
> context. The default implementation simply adapts the context to the
> field's interface and then uses simple attribute get/set. But a custom
> implementation for multilingual content could do this for all
> translations, or could make all the translations get/set
> language-independent fields from the canonical translation. The main
> problem with this approach is that it would only be a solution for
> z3c.form...just trying to access the field as an attribute of a
> Dexterity item does not use the data manager.
-1
Because if one were to manually set (i.e without a form) a
language-independent field, then the translation's fields won't be
updated.
> 3. Write a language-independent-field-aware property descriptor like:
> class LanguageIndependentProperty(object):
> def __init__(self, field):
> self.field = field
> def __get__(self, inst, type=None):
> # If inst is the canonical translation, get the value from the
> instance dict;
> # else find the canonical translation and get it from there
> def __set__(self, inst, value):
> # Find the canonical translation and set the value there
> This could be used in behavior adapters, or in custom subclasses of
> plone.dexterity.content.Item. The downside of this option is that either
> you need to put your language-independent fields in behaviors, or you
> need to create a custom class for your content type. (Since Dexterity
> doesn't auto-generate accessors and mutators like Archetypes did, and
> assumes values of the main schema can be reached via simple attribute
> access on the content item.)
So in your content type, you'd have to do:
Class Foo(dexterity.Item):
bar = LanguageIndependentProperty(barfield)
And a behavior would be doing something similar when it gets called?
+0 for custom class.
-1 for behavior since I think it's too onerous for the add-on developer.
> 4. Create a generic MultiLingualItem subclass of Item and override
> __getattribute__ and __setattr__ to Do The Right Thing for
> language-independent fields.
+1 This sounds like the best approach IMHO.
It's not too complicated for the add-on developer (who is used to using
special subclasses for LinguaPLone as well) and it's not z3c.form
specific.
Regards
--
JC Brand
http://opkode.com
--
You received this message because you are subscribed to the Google Groups "Dexterity development" group.
To post to this group, send email to dexterity-...@googlegroups.com.
To unsubscribe from this group, send email to dexterity-develo...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/dexterity-development?hl=en.
Although, if you have multiple (non lingua-related) event handlers with
many of them potentially changing field values, and you can't control
the execution order, then you could run into problems.
>
> 2 - Using z3c.form data manager. Only for z3cform, not native and
> orthogonal.
> -1
>
> 3 - LanguageIndependentProperty. Not orthogonal with dexterity.
> -1
>
> 4 - Create a generic MultiLingualItem subclass of Item and override
> __getattribute__ and __setattr__. Not orthogonal with dexterity
> -1
>
> 5 - What about using the grok directive and the xml importer in order
> to override the __getattribute__ and __setattr__ on the fields ? It'll
> not work when we add the marker interface manually, but can be
> acceptable.
> +1
__getattribute__ and __setattr__ are overridden if one wants to override
the reading and writing of an attribute on an instance.
This doesn't make sense in the case of Field objects. When a field value
is stored, it's not an attribute on the Field instance that gets stored.
It's an attribute on the content type.
For the fields, I think the correct methods to override would be *get*
and *set*.
See the class Field in zope.schema._bootstrapfields.py
The problem here is that these methods need to be called explicitly for
the override to work.
Something like this (writing from memory):
myobj.fields.getField('title').set(myobj, u"Hello World")
I think the z3c.form form machinery will call the *get* and *set*
methods, but what if the user just sets the value via attribute
assignment?
myobj.title = u"Hello World"
Then the overrides don't get called.
It would be nice if something like this worked, I'm just not sure how it
could...
JC
> To unsubscribe from this group, send email to dexterity-development
> +unsub...@googlegroups.com.
Yeah. And actually I don't think z3c.form does use the field's get/set.
It just adapts to get an IDataManager and then uses it. The default is
the attribute data manager which uses -- you guessed it -- attribute access.
>
> myobj.title = u"Hello World"
>
> Then the overrides don't get called.
>
> It would be nice if something like this worked, I'm just not sure how it
> could...
>
Right, which is what led me to __setattr__ and __getattribute__...
David
Yeah. And actually I don't think z3c.form does use the field's get/set. It just adapts to get an IDataManager and then uses it. The default is the attribute data manager which uses -- you guessed it -- attribute access.
For the fields, I think the correct methods to override would be *get*
and *set*.
See the class Field in zope.schema._bootstrapfields.py
The problem here is that these methods need to be called explicitly for
the override to work.
Something like this (writing from memory):
myobj.fields.getField('title').set(myobj, u"Hello World")
I think the z3c.form form machinery will call the *get* and *set*
methods, but what if the user just sets the value via attribute
assignment?
Right, which is what led me to __setattr__ and __getattribute__...
myobj.title = u"Hello World"
Then the overrides don't get called.
It would be nice if something like this worked, I'm just not sure how it
could...
David
--
You received this message because you are subscribed to the Google Groups "Dexterity development" group.
To post to this group, send email to dexterity-development@googlegroups.com.
To unsubscribe from this group, send email to dexterity-development+unsub...@googlegroups.com.