Adding a portal method the right way

Hi, I have a question to which I can think of a number of more or less hacky answers but I'm interested in how to do it right in modern Plone.

I'm trying to add a property of a selection type to groups. In doing so, I need to specify the name of a method (possible_values) that produces a list of selectable values:

    gdata = plone.api.portal.get_tool("portal_groupdata")
    gdata.manage_addProperty(
        "my_property", "possible_values", "multiple selection"
    )

Since the controlpanel template usergroups_groupdetails.pt is written such that it wants to access the method on its context, which is the portal, this method needs to be added to the portal in some way. In the past, a skin method could be used (this is how it used to be done by the application I'm working on modernising); I'd like to get rid of skins altogether.

Conceivable solutions include:

  • Overwrite the template. Besides being clumsy, this raises the question how the use case is really meant to be solved in Plone. I wouldn't want to start on the hypothesis that it isn't meant to or was overlooked.

  • Monkey-patch the method onto PloneSite. While simple and effective, this affects all potential Plone sites in the application. This may not be a practical problem but feels unclean.

  • Bite the bullet and keep a stripped-down skin just to address this technical quirk.

  • Do something fancy in the way of external methods, which sounds even more backwards than keeping a skin.

Moreover, the application used to have more skin scripts that I'm migrating away from. In some cases a more general refactoring is the best solution, but I still wonder how to provide methods that are as globally available as skin scripts. Of course, I'm aware that the best pattern is to write code to use browser views or adapters or such in the first place. Just curious...

Thank you for any ideas and insights!

Thomas Lotze via Plone Community wrote at 2022-7-20 07:54 +0000:

...
Since the controlpanel template usergroups_groupdetails.pt is written such that it wants to access the method on its context, which is the portal, this method needs to be added to the portal in some way. In the past, a skin method could be used (this is how it used to be done by the application I'm working on modernising); I'd like to get rid of skins altogether.

How does the template resolve the name into an object?
If it uses a path expression, the name could start with "@@"
which tells the expression to look it up as a view.
If it does not start with @@, a path expression usually
first looks it up as an attribute and if that fails tries
a view lookup.

Oh, I should have remembered that. Been too busy addressing views using @@ all the time, it seems. :wink: Thank you for the reminder, Dieter!

All right, that had been a bit of a quick reaction of mine. Turns out that while simple view lookup is a good solution in many cases, it leaves a number of them unaddressed. Embarrassingly, this includes the manage_addProperty call from my initial question.

During that call, the name of the select variable is being looked up by hasattr on the property manager, in that case the groupdata tool. The skin script would have been found by virtue of both being made accessible to attribute lookup on the portal, and acquisition for retrieving it from the portal while looking it up on the tool. I think having to provide it in the context of the tool now would be a nice case of doing things explicitly, but having to monkey-patch it there feels wrong.

Another case a simple view doesn't fit as a drop-in replacement for a skin script is templates in the context of some portal content that try to look up the method on their context rather than the portal. With skin scripts this used to work by acquisition as mentioned, but views aren't subject to acquisition. I think this is a good thing, but what's a good way to have them available everywhere then? Registering the method for multiple contexts is possible but if those become too many it feels unclean again.

Thank you for any further ideas.

Thomas Lotze via Plone Community wrote at 2022-7-24 20:55 +0000:

...
During that call, the name of the select variable is being looked up by hasattr on the property manager, in that case the groupdata tool. The skin script would have been found by virtue of both being made accessible to attribute lookup on the portal, and acquisition for retrieving it from the portal while looking it up on the tool. I think having to provide it in the context of the tool now would be a nice case of doing things explicitly, but having to monkey-patch it there feels wrong.

The PropertyManager is old (far predating the invention of views).
I think it should get modernized and support views as well as attributes
for "select variables".

Another case a simple view doesn't fit as a drop-in replacement for a skin script is templates in the context of some portal content that try to look up the method on their context rather than the portal. With skin scripts this used to work by acquisition as mentioned, but views aren't subject to acquisition. I think this is a good thing, but what's a good way to have them available everywhere then?

You register them for * (which means any interface).

The PropertyManager is old (far predating the invention of views).
I think it should get modernized and support views as well as attributes
for "select variables".

I agree. So I think it's OK for me to apply monkey-patches to it, knowing
that this is just a temporary solution (for suitable values of
"temporary" ;-)).

You register them for * (which means any interface).

Technically, yes; this is the way to do it with minimal amount of code to
write. Conceptually, it still feels unclean to clutter the view namespace
of every object. Maybe this is just the way it is, acquisition being a
lot worse in that regard in the first place...

Thank you!
Thomas