Mirroring or synchronizing the value of different registry records

good old sc.social.like needs to be updated to work in Plone 5; it kind of works already, but I have a problem with a new feature introduced in Plone 5: the social media configlet.

Plone 5 stores some information about Twitter and Facebook accounts associated with a site in registry records using the plone prefix: plone.facebook_app_id, plone.facebook_username and plone.twitter_username.

sc.social.like stores the same information on registry records using the ISocialLikeSettings interface. so, now I have 2 records for the same piece of information that can be updated from different places and are out of sync.

in the past I have managed to hack this using an invariant on the schema, to synchronize in one way:

and the updateWidgets() method in the configlet edit form, to synchronize on the other:

I was trying to use the same approach here, but I'm not being able to access the mentioned field widgets from the updateWidgets() because they are on a different fieldsets on the schema (facebook and twitter instead of default):

(Pdb) self.widgets.keys()
['enabled_portal_types', 'plugins_enabled', 'typebutton', 'do_not_track']
(Pdb) self.groups
[<plone.z3cform.fieldsets.group.GroupFactory object at 0x7f2c86580850>, <plone.z3cform.fieldsets.group.GroupFactory object at 0x7f2c86580c50>]
(Pdb) self.groups[0].fields.items()
[('fbaction', <Field 'fbaction'>), ('facebook_username', <Field 'facebook_username'>), ('facebook_app_id', <Field 'facebook_app_id'>), ('fbbuttons', <Field 'fbbuttons'>), ('fbshowlikes', <Field 'fbshowlikes'>)]

so, my question is actually 2:

  • is this a good approach or there is a saner way to mirror/synchronize the value of 2 different registry records
  • if this approach is fine, how can I change the value of a widget that is not in the default fieldset?

Why not have the add on use the Plone registry entries?

I though about that, but then I would have to visit 2 configlets to configure the package.

is there a way to display fields defined in one schema in another schema?

notice there are 4 fields using the plone prefix, while the other fields are using the sc.social.like.interfaces.ISocialLikeSettings prefix.

I think you could have your control panel use the other registrysettings.

Maybe it is something like this:

class SomeControlpanelSettingsEditForm(controlpanel.RegistryEditForm):


class SomeControlPanel(controlpanel.ControlPanelFormWrapper):
form = SomeControlpanelSettingsEditForm

and

alsoProvides(ISomeSettings, ISomeControlpanelSettingsProvider)

I have done this with:

but I am not sure if 'other control panels can do the same'

I tried using the AutoExtensibleForm feature of plone.autoform (configlet forms are auto extensible by default in Plone 5):

from Products.CMFPlone.interfaces import ISocialMediaSchema

class SocialLikeSettingsEditForm(controlpanel.RegistryEditForm):
    """Control panel edit form."""

    schema = ISocialLikeSettings
    label = _(u'Social Like')
    description = _(u'Settings for the sc.social.like package')
    additionalSchemata = (ISocialMediaSchema,)

but it failed with an error:

2017-03-10 16:20:57 ERROR Zope.SiteErrorLog 1489173657.970.758408054749 http://localhost:8080/Plone51/@@sociallike-settings
Traceback (innermost last):
  Module ZPublisher.Publish, line 138, in publish
  Module ZPublisher.mapply, line 77, in mapply
  Module ZPublisher.Publish, line 48, in call_object
  Module plone.z3cform.layout, line 63, in __call__
  Module plone.z3cform.layout, line 47, in update
  Module plone.z3cform.fieldsets.extensible, line 65, in update
  Module plone.z3cform.patch, line 30, in GroupForm_update
  Module z3c.form.group, line 132, in update
  Module z3c.form.form, line 136, in updateWidgets
  Module z3c.form.field, line 243, in update
  Module z3c.form.datamanager, line 102, in canWrite
  Module z3c.form.datamanager, line 66, in adapted_context
TypeError: ('Could not adapt', <RecordsProxy for sc.social.like.interfaces.ISocialLikeSettings>, <InterfaceClass Products.CMFPlone.interfaces.controlpanel.ISocialMediaSchema>)

then I found a cleaner approach using an event subscriber; it took me some time because of various corner cases, but I think is working fine:

now I just need to write some tests and wait for a better approach in the future.

UPDATE: this is an example of the output in the event log:

2017-03-10 16:14:36 INFO sc.social.like Processing: <Record sc.social.like.interfaces.ISocialLikeSettings.facebook_username>
2017-03-10 16:14:36 INFO sc.social.like <Record plone.facebook_username> was synchronized; new value is "plone"
2017-03-10 16:14:36 INFO sc.social.like Processing: <Record sc.social.like.interfaces.ISocialLikeSettings.facebook_app_id>
2017-03-10 16:14:36 INFO sc.social.like <Record plone.facebook_app_id> was synchronized; new value is "plone"
2017-03-10 16:14:36 INFO sc.social.like Processing: <Record sc.social.like.interfaces.ISocialLikeSettings.twitter_username>
2017-03-10 16:14:36 INFO sc.social.like <Record plone.twitter_username> was synchronized; new value is "plone"
2017-03-10 16:38:37 INFO sc.social.like Processing: <Record plone.share_social_data>
2017-03-10 16:38:37 INFO sc.social.like No need to synchronize

I need to change that logger.debug() instead.

1 Like