Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

allow brains in vocabularies (used for related Items) #29

Closed
wants to merge 2 commits into from

3 participants

@pbauer
Collaborator

same as #28 in master: Using the in-and-out widget for Relations was no longer possible after the last release. This fixes it.

@pbauer
Collaborator

Can someone please review this?

@davisagli
Owner

This looks a bit odd to me, but I'm missing the big picture. What code is expecting to find brains in a DisplayList?

@pbauer
Collaborator

It is not a DisplayList but a Vocabulary (which is a subclass of DisplayList) and the problem is that Products.Archetypes.browser.utils.Utils.translate gets the value of the vocabulary with vocab_value = vocab.getValue(v, original)
I use the in-and-out widget for references in a project (it is a subclass of ReferenceWidget) and when displaying the widget in the non-canonical language the said translate-method is called and fails since the vocabulary used to populate the widget contains brains.

@mauritsvanrees
Collaborator

I changed the relatedItems field of ATContentTypes to an InAndOutWidget, but this did not trigger an exception.
Is there an add-on that we can add to test this? Or maybe you can paste your field definition? Do you have a traceback?

Your fix accepts ImplicitAcquisitionWrapper, which can be brains or lots of other things, so that accepts more than we may want. The existing condition and failure message is still correct: DisplayLists, including Vocabularies have either strings or integers as keys. With your fix I guess that the return value is always going to be the default argument, because the key will never be found. Well, a test on repr(key) == repr(k) is then tried for each item, which might give a valid value.

I wonder if it may be better to keep the current condition and replace the TypeError with return default. With that change, the tests still pass with Archetypes 1.9.x on Plone 4.3. Sounds okay.

@pbauer
Collaborator

The error happens on editing the translation of a content-type with a cusotm relation-field with a custom vocabulary and the in-and-out-widget. Specificaly it happens when rendering the value of the canonical item on the left side of the translation-view and accessor() returns a brain in inandout.pt:

<tal:block
      tal:condition="not:python:hasattr(field, 'relationship')"
      tal:define="vocab python:field.Vocabulary(here);
                  value python:accessor();
                  display python:context.displayValue(vocab, value, widget);"
      tal:replace="structure display" />

This is the field:

atapi.ReferenceField(
    'selectedprojektleiter',
    relationship='projekt-leiter',
    multiValued=True,
    storage=atapi.AnnotationStorage(),
    vocabulary_factory=u"my.project.ListeMitarbeiter",
    widget=atapi.InAndOutWidget(
        label=_(u"Leiter des Projekts"),
        ),
),

This is the vocabulary my.project.ListeMitarbeiter:

class CurrentMitarbeiterVocabularyFactory(object):
    implements(IVocabularyFactory)

    def __call__(self, context):
        catalog = getToolByName(context, 'portal_catalog')
        items = []
        brains = catalog(object_provides=IMitarbeiter.__identifier__,
                         review_state="published",
                         sort_on='sortable_title')
        for r in brains:
            items.append(SimpleTerm(r.UID, r.UID, r.Title))
        return SimpleVocabulary(items)

CurrentMitarbeiterVocabularyFactory = CurrentMitarbeiterVocabularyFactory()

Here is the full traceback:

2013-09-20 09:44:46 ERROR Zope.SiteErrorLog 1379663086.020.146188654408 http://127.0.0.1:8080/DIPF2/en/research/projects/recognition-situations-in-urban-primary-schools/translate_item
Traceback (innermost last):
  Module ZPublisher.Publish, line 138, in publish
  Module ZPublisher.mapply, line 77, in mapply
  Module Products.PDBDebugMode.runcall, line 70, in pdb_runcall
  Module ZPublisher.Publish, line 48, in call_object
  Module Products.CMFFormController.FSControllerPageTemplate, line 91, in __call__
  Module Products.CMFFormController.BaseControllerPageTemplate, line 31, in _call
  Module Shared.DC.Scripts.Bindings, line 322, in __call__
  Module Shared.DC.Scripts.Bindings, line 359, in _bindAndExec
  Module Products.CMFCore.FSPageTemplate, line 237, in _exec
  Module Products.CMFCore.FSPageTemplate, line 177, in pt_render
  Module Products.PageTemplates.PageTemplate, line 79, in pt_render
  Module zope.pagetemplate.pagetemplate, line 132, in pt_render
   - Warning: Macro expansion failed
   - Warning: <type 'exceptions.KeyError'>: 'macro'
  Module zope.pagetemplate.pagetemplate, line 240, in __call__
  Module zope.tal.talinterpreter, line 271, in __call__
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 858, in do_defineMacro
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 533, in do_optTag_tal
  Module zope.tal.talinterpreter, line 518, in do_optTag
  Module zope.tal.talinterpreter, line 513, in no_tag
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 888, in do_useMacro
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 533, in do_optTag_tal
  Module zope.tal.talinterpreter, line 518, in do_optTag
  Module zope.tal.talinterpreter, line 513, in no_tag
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 954, in do_defineSlot
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 533, in do_optTag_tal
  Module zope.tal.talinterpreter, line 518, in do_optTag
  Module zope.tal.talinterpreter, line 513, in no_tag
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 858, in do_defineMacro
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 954, in do_defineSlot
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 533, in do_optTag_tal
  Module zope.tal.talinterpreter, line 518, in do_optTag
  Module zope.tal.talinterpreter, line 513, in no_tag
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 946, in do_defineSlot
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 533, in do_optTag_tal
  Module zope.tal.talinterpreter, line 518, in do_optTag
  Module zope.tal.talinterpreter, line 513, in no_tag
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 858, in do_defineMacro
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 533, in do_optTag_tal
  Module zope.tal.talinterpreter, line 518, in do_optTag
  Module zope.tal.talinterpreter, line 513, in no_tag
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 888, in do_useMacro
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 852, in do_condition
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 946, in do_defineSlot
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 533, in do_optTag_tal
  Module zope.tal.talinterpreter, line 518, in do_optTag
  Module zope.tal.talinterpreter, line 513, in no_tag
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 852, in do_condition
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 533, in do_optTag_tal
  Module zope.tal.talinterpreter, line 518, in do_optTag
  Module zope.tal.talinterpreter, line 513, in no_tag
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 821, in do_loop_tal
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 821, in do_loop_tal
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 533, in do_optTag_tal
  Module zope.tal.talinterpreter, line 518, in do_optTag
  Module zope.tal.talinterpreter, line 513, in no_tag
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 852, in do_condition
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 888, in do_useMacro
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 533, in do_optTag_tal
  Module zope.tal.talinterpreter, line 518, in do_optTag
  Module zope.tal.talinterpreter, line 513, in no_tag
  Module zope.tal.talinterpreter, line 343, in interpret
  Module zope.tal.talinterpreter, line 583, in do_setLocal_tal
  Module zope.tales.tales, line 696, in evaluate
   - URL: file:/Users/philip/workspace/dipf-www/src-mrd/Products.Archetypes/Products/Archetypes/skins/archetypes/widgets/inandout.pt
   - Line 37, Column 6
   - Expression: <PythonExpr context.displayValue(vocab, value, widget)>
   - Names:
      {'container': <Projekt at /DIPF2/en/research/projects/recognition-situations-in-urban-primary-schools>,
       'context': <Projekt at /DIPF2/en/research/projects/recognition-situations-in-urban-primary-schools>,
       'default': <object object at 0x10536eb70>,
       'here': <Projekt at /DIPF2/en/research/projects/recognition-situations-in-urban-primary-schools>,
       'loop': {u'field': <Products.PageTemplates.Expressions.PathIterator object at 0x10ecd9b90>,
                u'fieldset': <Products.PageTemplates.Expressions.PathIterator object at 0x10ecdacd0>},
       'nothing': None,
       'options': {'args': (),
                   'state': <Products.CMFFormController.ControllerState.ControllerState object at 0x10e635390>},
       'repeat': <Products.PageTemplates.Expressions.SafeMapping object at 0x10ec74730>,
       'request': <HTTPRequest, URL=http://127.0.0.1:8080/DIPF2/en/research/projects/recognition-situations-in-urban-primary-schools/translate_item>,
       'root': <Application at >,
       'template': <FSControllerPageTemplate at /DIPF2/en/research/projects/recognition-situations-in-urban-primary-schools/translate_item>,
       'traverse_subpath': [],
       'user': <PloneUser 'pbauer'>}
  Module Products.PageTemplates.ZRPythonExpr, line 48, in __call__
   - __traceback_info__: context.displayValue(vocab, value, widget)
  Module PythonExpr, line 1, in <expression>
  Module Products.CMFCore.FSPythonScript, line 127, in __call__
  Module Shared.DC.Scripts.Bindings, line 322, in __call__
  Module Shared.DC.Scripts.Bindings, line 359, in _bindAndExec
  Module Products.PythonScripts.PythonScript, line 344, in _exec
  Module script, line 2, in displayValue
   - <FSPythonScript at /DIPF2/en/research/projects/recognition-situations-in-urban-primary-schools/displayValue>
   - Line 2
  Module Products.Archetypes.browser.utils, line 52, in translate
  Module Products.Archetypes.utils, line 565, in getValue
TypeError: DisplayList keys must be strings or ints, got <type 'Acquisition.ImplicitAcquisitionWrapper'>
@pbauer
Collaborator

Actually, the longer I look at it the more confused I get.

I don't really understand what

<tal:block
      tal:condition="not:python:hasattr(field, 'relationship')"
      tal:define="vocab python:field.Vocabulary(here);
                  value python:accessor();
                  display python:context.displayValue(vocab, value, widget);"
      tal:replace="structure display" />

is supposed to do but I see that there is a nesting-error since tal:condition is evaluated after tal:define. It should be:

<tal:block tal:condition="not:python:hasattr(field, 'relationship')">
  <span tal:define="vocab python:field.Vocabulary(here);
                    value python:accessor();
                    display python:context.displayValue(vocab, value, widget);"
        tal:replace="structure display" /> 
</tal:block>

What gets displayed (by the structures above that) is in fact the value of the translated objects relation-field and not of the canonical one as it sould be.

@pbauer
Collaborator

I fixed that nesting-bug in branch 1.9.x: 80d2c1f

@pbauer
Collaborator

After fixing the nesting this pull-request is obsolete.
The other issue (the wrong value being displayed in the translation-view) is the same for InAndOutWidget and ReferenceWidget.

@pbauer
Collaborator

To finish the story here: I won't pursue the problem above since (previously unknown to me) LinguaPlone has a ReferenceCriterionSyncer that uses the method translated_references to automatically link translated items to the translation of the relation of the canonical object. Wow.

@pbauer pbauer closed this
@mauritsvanrees
Collaborator

Thanks for fixing the nesting bug. Works fine now when I test it with a similar setup. Can you merge that to master?

@pbauer
Collaborator

merged to master

@pbauer pbauer deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 11, 2013
  1. @pbauer

    pep8

    pbauer authored
  2. @pbauer
This page is out of date. Refresh to see the latest.
Showing with 18 additions and 11 deletions.
  1. +5 −2 CHANGES.txt
  2. +13 −9 Products/Archetypes/utils.py
View
7 CHANGES.txt
@@ -4,6 +4,9 @@ Changelog
1.9.5 (unreleased)
------------------
+- Allow brains (used for related items in an in-and-out-widget)
+ [pbauer]
+
- Various vocabulary fixes, mostly for translations and
IntDisplayLists.
[maurits]
@@ -12,7 +15,7 @@ Changelog
1.9.4 (2013-08-29)
------------------
-- Fixed error of validate_content_types when checking a field that was an
+- Fixed error of validate_content_types when checking a field that was an
instance of OFS.Image.File
[ichim-david]
@@ -23,7 +26,7 @@ Changelog
1.9.3 (2013-08-14)
------------------
-- Avoid UnicodeDecodeError in @@at_utils.translate if the value contains
+- Avoid UnicodeDecodeError in @@at_utils.translate if the value contains
special chars
[gbastien]
View
22 Products/Archetypes/utils.py
@@ -8,7 +8,6 @@
import transaction
from zope.component import getUtility
-from zope.i18n import translate
from zope.i18nmessageid import Message
from AccessControl import ClassSecurityInfo
@@ -16,6 +15,7 @@
from AccessControl.SecurityInfo import ACCESS_PUBLIC
from Acquisition import aq_base, aq_inner, aq_parent
+from Acquisition import ImplicitAcquisitionWrapper
from ExtensionClass import ExtensionClass
from App.class_init import InitializeClass
from Products.CMFCore.utils import getToolByName
@@ -357,7 +357,8 @@ def add(self, key, value, msgid=None):
def getKey(self, value, default=None):
"""get key"""
v = self._values.get(value, None)
- if v: return v[1]
+ if v:
+ return v[1]
for k, v in self._values.items():
if repr(value) == repr(k):
return v[1]
@@ -369,7 +370,8 @@ def getValue(self, key, default=None):
raise TypeError('DisplayList keys must be strings, got %s' %
type(key))
v = self._keys.get(key, None)
- if v: return v[1]
+ if v:
+ return v[1]
for k, v in self._keys.items():
if repr(key) == repr(k):
return v[1]
@@ -416,7 +418,7 @@ def _cmp(a, b):
def __cmp__(self, dest):
if not isinstance(dest, DisplayList):
- raise TypeError, 'Cannot compare DisplayList to %s' % (type(dest))
+ raise TypeError('Cannot compare DisplayList to %s' % (type(dest)))
return cmp(self.sortedByKey()[:], dest.sortedByKey()[:])
@@ -532,7 +534,8 @@ def getValue(self, key, default=None):
else:
raise TypeError("Key must be string or int")
v = self._keys.get(key, None)
- if v: return v[1]
+ if v:
+ return v[1]
for k, v in self._keys.items():
if repr(key) == repr(k):
return v[1]
@@ -560,8 +563,9 @@ def getValue(self, key, default=None):
"""
Get i18n value
"""
- if not isinstance(key, basestring) and not isinstance(key, int):
- raise TypeError('DisplayList keys must be strings or ints, got %s' %
+ if (not isinstance(key, basestring) and not isinstance(key, int) and not
+ isinstance(key, ImplicitAcquisitionWrapper)):
+ raise TypeError('DisplayList keys must be strings, ints or brains, got %s' %
type(key))
v = self._keys.get(key, None)
value = default
@@ -659,7 +663,7 @@ def setdefault(self, key, failobj=None):
def popitem(self):
if not self.data:
- raise KeyError, 'dictionary is empty'
+ raise KeyError('dictionary is empty')
k = self._keys.pop()
v = self.data.get(k)
del self.data[k]
@@ -756,7 +760,7 @@ def wrap_method(klass, name, method, pattern='__at_wrapped_%s__'):
def unwrap_method(klass, name):
old_method = getattr(klass, name)
if not isWrapperMethod(old_method):
- raise ValueError, ('Non-wrapped method %s.%s' % (klass.__name__, name))
+ raise ValueError('Non-wrapped method %s.%s' % (klass.__name__, name))
orig_name = getattr(old_method, ORIG_NAME)
new_method = getattr(klass, orig_name)
delattr(klass, orig_name)
Something went wrong with that request. Please try again.