Ticket #14266 (new Bug)
Traversal failure in folderish content on name collision with a field
Reported by: | seanupton | Owned by: | davisagli |
---|---|---|---|
Priority: | minor | Milestone: | 4.x |
Component: | Dexterity | Version: | 4.3 |
Keywords: | Cc: |
Description (last modified by seanupton) (diff)
OFS.Traversable.unrestrictedTraverse() and ZPublisher.BaseRequest.DefaultPublishTraverse.publishTraverse() both favor getattr(obj, name) before trying obj.__getitem__(name).
This can have a side-effect I consider a user-facing defect in (at least) Dexterity-based folderish types: users are unable to traverse to newly added content when the id of the item they added collides with a field name on the folderish object. The item will appear in navigation and folder contents, but be inaccessible to work with, rename, or delete.
Steps to duplicate
(1) Create a folderish Dexterity type based on plone.dexterity.content.Container (in add-on or TTW). (2) Add a field called "contacts". (3) Add a content item of this type. (4) In that content item, add any content item with the title of "Contacts" -- the auto-generated shortname of "contacts" should have a collision with the field, but you would not know until... (5) Try to click into that content item from the navigation.
Workarounds
Write a custom traversal adapter ( example) or __bobo_traverse__ method.
Is this really a bug?
IMHO, yes, if a user can add content that they cannot then access. Either deny the user that ability to add that name, auto-choose a name that does not collide, or fix traversal to favor __getitem__ first. Moreover, you cannot rename or delete the item.
Scope
- This may be more of an issue with Dexterity content than in Archetypes content, depending upon how fields are named. I suspect this is most likely to affect add-on authors, but may also affect plone.app.contenttypes for Plone 5.
Proposed solution(s)
I am unsure of the suitability of this, but have a hunch it will work; please comment if you have thoughts on it:
- Modify plone.dexterity.browser.traversal.DexterityPublishTraverse.publishTraverse() to avoid deferring to DefaultPublishTraverse when the context provides IDexterityContainer and the name requested is in self.context.objectIds().
- Instead obtain the object via self.context.get(name) and return it.