4

in a Plone add-on product, I have a control panel page where some config options can be set. They are stored in plone.registry. The control panel adapter fetches the different fields in its __init__ method by querying the interface, like:

class MultiLanguageExtraOptionsAdapter(LanguageControlPanelAdapter):
    implementsOnly(IMultiLanguageExtraOptionsSchema)

    def __init__(self, context):
        super(MultiLanguageExtraOptionsAdapter, self).__init__(context)
        self.registry = getUtility(IRegistry)
        self.settings = self.registry.forInterface(
            IMultiLanguageExtraOptionsSchema)

Now I add an additional field to the interface IMultiLanguageExtraOptionsSchema and restart plone. On the control panel page I then an error:

KeyError: 'Interface `plone.app.multilingual.interfaces.IMultiLanguageExtraOptionsSchema` defines a field `blah`, for which there is no record.'

(This is expected for the forInterfacemethod , as described on the plone.registry README. The record is not there.)

Of course, if I add that field via GenericSetup (registry.xml), and I re-install the product / re-run the "Control Panel" step, all is well:

<registry>
 <records interface="plone.app.multilingual.interfaces.IMultiLanguageExtraOptionsSchema">
   <value key="blah"></value>
 <records>
<registry>

But I don't want to force users to re-install a product, just because there's a new option in the product-specific control panel. So my question: Is there a recommended way for getting a new record for a new field into plone.registry?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
pysailor
  • 43
  • 3

3 Answers3

7

You could try/catch the KeyError and then make sure all registry settings are registered:

try:
    self.settings = self.registry.forInterface(IMultiLanguageExtraOptionsSchema)
except KeyError:
    registry = getUtility(IRegistry)
    registry.registerInterface(IMultiLanguageExtraOptionsSchema)

I would recommend to write an upgrade step though (which would force your users to reinstall the product of course).

upgrades.py:

def update_registry(context):
    registry = getUtility(IRegistry)
    registry.registerInterface(IMultiLanguageExtraOptionsSchema)

upgrades.zcml::

  <genericsetup:upgradeStep
      source="*"
      destination="1100"
      title="Update plone.app.multilingual setting registry"
      description=""
      profile="plone.app.multilingual:default"
      handler=".upgrades.update_registry"
      />

See

https://github.com/collective/collective.mailchimp/blob/master/collective/mailchimp/upgrades.py

and

https://github.com/collective/collective.mailchimp/blob/master/collective/mailchimp/upgrades.zcml

for an example.

tisto
  • 1,337
  • 7
  • 13
  • Thanks, I had also thought of an upgrade step, but IMHO from the user's point of view there's not much difference to re-installing. I just remembered the "bad old days" (pre plone.registry) when it was easily possible to add new settings & have them initialized without try/excepts or user interactions. – pysailor Mar 12 '13 at 19:43
  • +1 for nice answer. @pysailor: Are you referring to skin-folders? – Ida Mar 13 '13 at 06:40
5

If you pass False as the second parameter to forInterface:

registry.forInterface(IMultiLanguageExtraOptionsSchema, False)

then it won't throw an error if fields from the schema are missing from the registry, but will simply return the field's default value.

David Glick
  • 5,422
  • 17
  • 23
  • Thanks, adding `check=False` works for displaying the control panel. But trying to save (=set a new value) will result in an AttributeError. My solution for now: in the set_blah method inside the control panel adapter, I check if the registry already has a record of the name in question, and if not, I create a plone.registry.Record() (as described in the README) and save it with a default value. This happens before the actual setting of the value passed in from the form (`self.setting.blah = value`) – pysailor Mar 13 '13 at 08:44
2

Safe getting settings from registry:

def get_registry_settings(interface, name):
    registry = getUtility(IRegistry)
    settings = registry.forInterface(interface, check=False)
    value = getattr(settings, name)
    if value == settings.__schema__[name].missing_value:
        value = settings.__schema__[name].default
    return value