Custom settings in the settings pane of the content builder

Hi there,

I have been doing some experiments in Pro to see if it’s possible at all to hook in and add custom settings to the page settings pane within the Pro content builder. I want to do this so that it could be possible to manage a select few Advanced Custom Field options from within Pro, rather than having to come back out to the WordPress editor to make that kind of change.

In v3 of Pro, I was able to create some custom settings for the Page Settings dialogue box that popped up. I achieved this by extending the Cornerstone_Legacy_Setting_Section class and hooking the registration into the cornerstone_register_setting_sections action. This no longer works for me in Pro v4, and the other settings panes that are built-in to Pro (slider-above, slider below and x-settings) don’t show up in the Page Settings pane either.

After some tinkering and experimentation, I have been able to create a bunch of controls in the settings section, copying the structure of the controls I had on a custom Element. I’ve got the following sorted:


This was done by filtering in on the cs_builder_settings_controls filter.

I even managed to make sure that the values are populated from the post on load, using the cs_content_load_settings filter and grabbing the data from the Advanced Custom Fields data that was set in the WordPress editor.

This is all great, however I have not been able to find a way to update the ACF fields again when the post is saved. I found the two filters above in the Cornerstone_Content class, so looking in there I was able to see that other settings (Title, slug, status) are being set within the class here, in the set_settings function, but there is no hook or filter available to either add my custom settings to the mix, or just hook in to the new values so that they can be transferred over to ACF (in my use case there is no real need to save the settings into the _cornerstone_settings postmeta).

I have managed to make it all work as needed for me by adding an action hook into the set_settings function at line 305 or thereabouts:
do_action('cs_content_set_settings', $this->id, $settings );

With this I was then able to hook in and grab the custom settings that are being updated and send their value over to ACF to be saved like so:

function cxx_content_set_settings( $post_id, $settings ) {
  if ( isset($settings['info_content']) ) {
    update_field( 'info_content', $settings['info_content'], $post_id );
  }
}
add_action( 'cs_content_set_settings', 'cxx_content_set_settings', 10, 2 );

This may or may not be the best place to put that action / hook, but if it is, it would be wonderful for it (or something similar) to be integrated into Pro core so that even more great functionality can be handled and sorted out within Pro!

Also if I have missed a better way of doing this via another filter / hook that exists, I would love to know!
Cheers

Hi @arthurodb,

Nice job getting that all working! It looks awesome, and you’re definitely on the right track using all those filters. We did intentionally remove the other settings from the builder but they can still be managed in the WordPress post editor.

We try to add hooks liberally in places like that to give plugin developers room to creatively expand on things. So while you are working with some pretty low level hooks, there’s certainly nothing wrong with your implementation. Regretfully we don’t have a first class API that makes what you’re doing here easier and more structured. Pro 4 was the first time our control mapping systems were 100% unified. Prior to that, we had slightly different API implementations for the Inspector, Settings, and Theme Options which was a bit of a mess to keep up with. It’s not really a big priority to do a first class API here, but you will probably see it starting to evolve over the next few cycles. Things will take more shape when we do the Theme Options reboot. We’re going to explore having a large number of global settings available, then allow them to be modified on a page level.

Hi Alexander,

Thanks for your response.
Totally understand the changes that have happened in the code structure and control systems. It’s certainly allowing for a load more customisation and in-depth controls for elements!

I guess I’m just requesting that one extra filter or hook be added into the code, the dummy one that I added of do_action('cs_content_set_settings', $this->id, $settings ); into the set_settings function of the Cornerstone_Content class file.

Currently this is an edit I’m making to a core Pro file, which will be overridden with any update, and that’s what I’m hoping to avoid if it were to be added into Pro core. All the other changes being made were able to be added through filters / hooks in the child theme. Though I appreciate that I may be in the tiny minority of people who want to extend Pro in this way, so adding in a hook just for us and our uses in website development may be a bit out of scope of support!

Cheers either way!

Hey @arthurodb,

I gotcha! I’m sorry for missing that the first time around. I assumed those hooks were already there… seems like they should have been all along. I see what you mean now.

We can definitely get something in there to accommodate this. Sometimes it takes developers like yourself looking for ways to extend things to expose where new hooks may be needed. Really glad you’ve brought it up because it will help make Cornerstone and Pro more extensible for everyone.

I do have a concern about the exact placement being in the set_settings method because in some cases that is called before the ID is available. We have these lines:

$settings_update = cs_define_defaults( array_merge($previous_settings, $settings), $this->default_settings());

cs_update_serialized_post_meta( $this->id, '_cornerstone_settings', $settings_update, '', false, 'cs_content_update_serialized_content' );

Would it work on your end to do something like this?

$settings_update = cs_define_defaults( array_merge($previous_settings, $settings), $this->default_settings());

do_action( 'cs_content_update_settings', $settings_update, $this-> id );

cs_update_serialized_post_meta( $this->id, '_cornerstone_settings', $settings_update, '', false, 'cs_content_update_serialized_content' );

Let me know if that works on your end.

Hi @alexander,

No worries at all, and thanks for looking into it further for me.
I have tinkered around with the hook being in that location instead, but there is an issue there. From what I could work out, the cs_define_defaults() function is stripping away any entries in the merged $previous_settings and $settings array that aren’t matched via array key in the $this->default_settings() return. Currently this return, found at Cornerstone_settings::default_settings(), is limited to custom_css, custom_js and responsive_text.

With some further looking, I was able to sort out this restriction by adding another filter to the Cornerstone_Settings::default_settings() function to allow adding extra array keys to the $defaults whitelist being returned:

return apply_filters('cs_content_return_default_settings', $defaults, $this->post_type );

instead of just

return $defaults;

I’ve called this filter cs_content_return_default_settings as the similar name of cs_content_default_settings is already in use in the Cornerstone_Settings::get_default_settings() function.

By adding the filter to the $defaults being returned by default_settings(), I was able to get access to the settings being saved and update the ACF fields and everything worked swimingly!

One side-effect is that the new settings I am adding then also get added to the _cornerstone_settings meta value for the post through the cs_update_serialized_post_meta() function. Another approach which would work around this would be to offer both the non-whitelisted $settings variable and the whitelisted $settings_update variable to the hook like so:

do_action( 'cs_content_update_settings', $settings, $settings_update, $this->id );

Then one would have access to the cleaned settings that will ultimately end up in the database, or just the values that are being parsed by the Settings pane within Pro, but which don’t need to be saved to the database in the _cornerstone_settings meta value.

Hope that all makes sense. Do let me know your thoughts, if this is not all too much customisation and beyond-scope stuff!

Cheers

This is great stuff! Thank you so much.

I think this is probably redundant and perhaps a bit heavy handed.

$settings_update = cs_define_defaults( array_merge($previous_settings, $settings), $this->default_settings());

I’m considering just removing the cs_define_defaults call and simply doing array_merge. To your point about not being able to clean the value, I think we can further simplify things:

$settings_update = apply_filters( 'cs_content_settings_update', array_merge($previous_settings, $settings), $this->id );
cs_update_serialized_post_meta( $this->id, '_cornerstone_settings', $settings_update, '', false, 'cs_content_update_serialized_content' );

At that point you could clean the value inside the filter like:

add_filter('cs_content_settings_update', function( $settings, $id ) {

  $value = $settings['thing'];
  unset( $settings['thing']);
  // do something with $value and $id

}, 10, 2);

Would you mind giving that variation a try? If that works on your end, I’ll do a bit more testing and see about merging it for the first release of the next cycle. Thank you!

1 Like

This has worked perfectly for me, and I agree, looks a little bit more straightforward than what I was suggesting with the extra filters and other parts.
Thanks for your help in this! I look forward to seeing it in the next cycle.

Thanks @arthurodb! I appreciate you testing that out. I meant to confirm here afterwards, but I was able to get this into the 4.1.5 release.

1 Like