plone / mockup

A collection of client side patterns for faster and easier web development
http://plone.github.io/mockup/
BSD 3-Clause "New" or "Revised" License
46 stars 91 forks source link

Missing documentation on how to configure the RelatedItemsWidget (from plone.app.widgets) #143

Closed pbauer closed 10 years ago

pbauer commented 10 years ago

How do I configure the RelatedItemsWidget to let me only choose items from a vocabulary_factory? I could not find any pointers.

Old code:

    atapi.ReferenceField('selectedmitglieder',
        relationship='projekt-mitglied',
        multiValued=True,
        storage=atapi.AnnotationStorage(),
        vocabulary_factory=u"mypackage.StaffMembers",
        enforceVocabulary=True,
        widget=atapi.ReferenceWidget(label=u"Members",)
       ),

This gives me a select-list with names as constructed in mypackage.StaffMembers.

New code:

    atapi.ReferenceField('selectedmitglieder',
        relationship='projekt-mitglied',
        multiValued=True,
        storage=atapi.AnnotationStorage(),
        vocabulary_factory=u"mypackage.StaffMembers",
        enforceVocabulary=True,
        widget=at.RelatedItemsWidget(label=u"Members",)
       ),

This code gives me the new RelatedItemsWidget (at-flavor) and my vocabulary_factory is ignored. What I want is a select2-based widget with only my values as possible reference-targets and without the whole search/browse-stuff.

vangheem commented 10 years ago

You're probably getting an error retrieving the vocabulary data via ajax.

Unfortunately(or fortunately, however you look at it), we're very strict for which vocabularies we allow via ajax requests. We were afraid we might leak data otherwise.

Thank you for bringing this up because the way we do lookups right now is somewhat broken.

In the meantime, I think you should be able to do something like this:

from plone.app.widgets import browser
browser._permissions['mypackage.StaffMembers'] = 'View'

and then your vocabulary should work.

We'll have to think of a better way to do this and still maintain security obviously...

vangheem commented 10 years ago

Any ideas anyone else as to a better way to do this?

Also, another problem, we only do permission checks against the root of the site since the getVocabulary view is only setup to work against the root. This should be moved to work against any context; however, then that opens us up to a potential DOS avenue since the plone.app.vocabularies.Catalog vocabulary only requires "View" permission... Essentially, we'd be opening up an API to plone that we might not want to.

Is it reasonable to restrict getVocabulary to logged in users? Then, set default vocabulary access (for all vocabularies) to "Modify portal content" against the real context of the widget (not the always portal root which it is right now). If vocabularies require a different permission, ONLY then do we do the permission check other than "Modify portal content".

Is there any reason the getVocabulary view should be available to anonymous users? IMO, it should not.

vangheem commented 10 years ago

@pbauer also, you should be using the select2 widget instead of related items. Related items is only for selecting content from the catalog--so you really shouldn't be using vocabulary_factory with it.

davisagli commented 10 years ago

Would it be feasible to reference a field instead of a vocabulary? Then the field's edit permission could be checked to determine if its vocabulary should be accessible.

vangheem commented 10 years ago

We tried to make it simple and generic. For instance, getVocabulary is also used for live search and eventually /@@search.

So it wouldn't work for those cases.

I suppose the catalog could be a special case though and all other vocabularies are checked against the field's permissions.

djay commented 10 years ago

There's no reason relateditemswidget can't be used for hierarchical non-content vocabularies. That was the original intention anyway.

pbauer commented 10 years ago

True, I get {"error": "Vocabulary lookup not allowed"} when calling the url in the ajaxvocabulary-parameter. I missed that. With the permission set to "View" I get this:

  Module plone.app.widgets.browser, line 157, in __call__
  Module inspect, line 813, in getargspec
TypeError: <method-wrapper '__call__' of function object at 0x10bfb0e60> is not a Python function

When using the select2-widget as suggested the existing values in the widgets are only UUIDs and not the titles of the objects (the RelatedItemsWidget shows the title). Where does the RelatedItemsWidget transform these to titles?

The select2-widget actually showed up after I worked around

  Module plone.app.widgets.at, line 46, in edit
  Module plone.app.widgets.at, line 212, in _widget_args
  Module plone.app.widgets.at, line 194, in getWidgetValue
TypeError: sequence item 0: expected string, Acquisition.ImplicitAcquisitionWrapper found

by using the getWidgetValue-method from the RelatedItemsWidget when the field is a reference-field.

But as you said it did not work right because of the vocabulary.

vangheem commented 10 years ago

Okay, I didn't realize that your vocabulary was a vocabulary of objects--I should have known better as I just now noticed you were using ReferenceField. You should go back to using relatedItems. There are two ways you can go:

And yes, the integration part of Plone and patterns aren't documented at all. Mostly because it's going to change quite a bit in the following months.

We haven't documented at all how to use your own zope3 vocabulary or how you setup any of the widgets outside their base use-cases we're hitting right now.

So expect some rough edges. Read plone.app.widgets for hints at how it's all wired up.

Thanks!

pbauer commented 10 years ago

I created the pull-request https://github.com/plone/plone.app.widgets/pull/39 to actually use the custom vocabulary. That only kinda works ;-)

  1. On editing the widget is pre-populated with all the items from the vocabulary (meaning they are selected and will also saved as values when I click save).
  2. When editing after saving with all items selected I get an exception because the HTML-Headers are to long:
ERROR ZServer uncaptured python exception, closing channel 
<ZServer.HTTPServer.zhttp_channel connected 127.0.0.1:53898 at 0x10f010128 channel#: 8 requests:> 
(<type 'exceptions.ValueError'>:HTTP headers invalid (too long) 
  (got: 12288 bytes, allowed 8192 bytes 
    [/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/asyncore.py|read|83] 
    [/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/asyncore.py|handle_read_event|442] 
    [/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/asynchat.py|handle_read|170] 
    [/Users/philip/.cache/buildout/eggs/Zope2-2.13.20-py2.7.egg/ZServer/HTTPServer.py|collect_incoming_data|435])
  1. The widget displays the title and the path of the brains that are the values of the vocabulary-terms but disregards the title of the vocabulaty-terms that I customized to also show the surname.
pbauer commented 10 years ago

More issues:

  1. The widget still shows the search and browse-tabs (allhough the have the same content)
  2. The search does not work, I always get all results.
garbas commented 10 years ago

@dextermilo could you take a look if this issues is still valid with all changes you did to related items? @pbauer is documentation better now? -> http://plone.github.io/mockup/dev/#patterns/relateditems

pbauer commented 10 years ago

@garbas the documentation does not cover the plone-specifics, but maybe that belongs somewhere else.

We have a open pull-request for plone.app.vocabularies (https://github.com/plone/plone.app.vocabularies/pull/4) that allows you to use the (great) widget with your own vocabularies provided they use the sliceableVocabulary. Here is some example-code for this:

from five import grok
from plone.app.querystring.queryparser import parseFormquery
from plone.app.vocabularies import SlicableVocabulary
from plone.app.widgets import browser
from zope.schema.interfaces import IVocabularyFactory
from zope.schema.vocabulary import SimpleVocabulary
from my.project.interfaces import Hero

class SimpleAndSlicableVocabulary(SimpleVocabulary, SlicableVocabulary):
    pass

class HeroesVocabulary(object):
    grok.implements(IVocabularyFactory)

    def __call__(self, context, query, batch=None):
        terms = []
        catalog = getToolByName(context, 'portal_catalog')
        parsed_query = parseFormquery(context, query['criteria'])
        brains = list(catalog(object_provides=IHero.__identifier__,
                              **parsed_query))
        for brain in brains:
            terms.append(SimpleVocabulary.createTerm(
                brain, str(brain.UID), brain.Title))

        return SimpleAndSlicableVocabulary(terms)

grok.global_utility(HeroesVocabulary, name=u"my.project.heroesvocabulary")
browser._permissions['my.project.heroesvocabulary'] = 'View'

The schema might then look like this:

from Products.Archetypes import atapi
from plone.app.widgets import at

MySchema = folder.ATFolderSchema.copy() + atapi.Schema((

atapi.ReferenceField(
    'myheroes',
    relationship='someones-heroes',
    multiValued=True,
    storage=atapi.AnnotationStorage(),
    vocabulary_factory=u"my.project.heroesvocabulary",
    widget=at.RelatedItemsWidget(
        label=_(u"Pick your heroes"),
    ),
)

We also changed the widget (archetypes only for the moment) so you can customize the a different view for vocabularies. The default-view (@@getVocabulary) shows title and path of the objects but sometimes you want something else (like surnames)

widget=at.RelatedItemsWidget(
    label=_(u"Heroes again"),
    vocabulary_view="@@getSurnameVocabulary",
),
garbas commented 10 years ago

@pbauer

pull request plone.app.vocabularies reviewed and merged. for plone related docs we need to bring back @@demo-widgets which @miohtama started few months ago. i've opened issues #177 so we dont forget