How to override existing field in Siteorigin?

809 views Asked by At

I want to make a custom field for the existing field type color, the default from Siteorigin widget bundle. Basically, what I need to create my own UI and backend logic for the color field type.

As this field is used in many places, if I could override the field type directly, will automatically work in every place.

I searched many times, and also dig through the source code of widgets bundle and SO itself, but couldn't find what I needed, even I tried to load my fields folder with 0 priority (highest) but didn't work. Its still displaying the default color field type.

Can anybody point me to some actions or filters, so that, I can de-register and re-register with the same type color? Or if any other workaround possible?


UPDATE

My question focus is for Siteorigin Widgets Bundle Plugin.

I don't want to just override any style variables, rather want to override existing fields. After some research, I found 2 filters that could be used to override existing field(s):

// Give other plugins a way to modify this form.
$form_options = apply_filters( 'siteorigin_widgets_form_options', $form_options, $this );
$form_options = apply_filters( 'siteorigin_widgets_form_options_' . $this->id_base, $form_options, $this );

The two filters get called in method form_options() in class SiteOrigin_Widget but this method is called like this:

public function form( $instance, $form_type = 'widget' ) {

    if( $form_type == 'widget' ) {
        if( empty( $this->form_options ) ) {
            $this->form_options = $this->form_options();
        }
        $form_options = $this->form_options;
    }

...
...

}

But it looks like $this->form_options is never empty and so, method $this->form_options() is never called which in turn never applies the filters siteorigin_widgets_form_options and siteorigin_widgets_form_options_<id_base>

And thus, my chance of modifying the form fields becomes zero with this approach.

What I basically need is for eg. there is an existing field color and I have another custom field adv-color. Now, the challenge is to override all instance of color field to adv-color and thus every instance of the field is overridden. But, it's still a hope.

Please let me know if my approach is wrong or is there is another way to solve this. Example(s) is/are greatly expected.

2

There are 2 answers

3
AudioBubble On BEST ANSWER

Can anybody point me to some actions or filters, so that, I can de-register and re-register with the same type color? Or if any other workaround possible?

I don't see a way to de-register and re-register the color field type (or generator).

However, if you want to customize the field's UI (or even completely change its look n' feel), you can create a PHP class which extends one of these classes: SiteOrigin_Widget_Field_Base, SiteOrigin_Widget_Field_Text_Input_Base, or SiteOrigin_Widget_Field_Color. See a simple example below:

// File: class-my-siteorigin-widget-field-color.php
class My_SiteOrigin_Widget_Field_Color extends SiteOrigin_Widget_Field_Color {

    public function enqueue_scripts() {
        // Enqueues custom CSS and/or JS files, if any.

        wp_enqueue_script( 'my-script', 'path/to/file.js', [ 'jquery' ], '20180601' );
        wp_enqueue_style( 'my-custom-stylesheet', 'path/to/file.css', [], '20180601' );
    }

    // Here you can modify the field's UI to your liking. Even a totally new UI.
    protected function render_field( $value, $instance ) {
        ?>
        <b>Text before</b>
        <input type="<?php echo esc_attr( $this->input_type ) ?>"
               name="<?php echo esc_attr( $this->element_name ) ?>"
               id="<?php echo esc_attr( $this->element_id ) ?>"
                 value="<?php echo esc_attr( $value ) ?>"
                 <?php $this->render_data_attributes( $this->get_input_data_attributes() ) ?>
                 <?php $this->render_CSS_classes( $this->get_input_classes() ) ?>
            <?php if ( ! empty( $this->placeholder ) ) echo 'placeholder="' . esc_attr( $this->placeholder ) . '"' ?>
            <?php if( ! empty( $this->readonly ) ) echo 'readonly' ?> />
        <i>Text after</i>
        <?php
    }

    // See `SiteOrigin_Widget_Field_Base` for all the properties and methods you
    // can extend. See also `SiteOrigin_Widget_Field_Text_Input_Base`, which is
    // being extended by `SiteOrigin_Widget_Field_Color`.
}

You should then load the class in/during the init hook in WordPress. Example:

add_action( 'init', function(){
    require_once __DIR__ . '/includes/class-my-siteorigin-widget-field-color.php';
} );

And then, to force color fields to use the custom class (above), add the class name prefix to the $prefixes array below:

add_filter( 'siteorigin_widgets_field_class_prefixes', function( $prefixes ){
    return array_merge( [ 'My_SiteOrigin_Widget_Field_' ], $prefixes );
} );

Unfortunately, you can't unset or remove SiteOrigin_Widget_Field_Color from the class list, because the above filter only allows you to filter the class name prefixes.

But in the example callback above, the custom My_SiteOrigin_Widget_Field_Color would be "seen" first by the SiteOrigin Widgets Bundle plugin; hence, color fields would use the custom class in place of the default one — i.e. SiteOrigin_Widget_Field_Color.

For more details on that, see SiteOrigin_Widget_Field_Factory::get_class_prefixes() and the entire SiteOrigin_Widget_Field_Factory source code. And SiteOrigin_Widget::form().

See also:

UPDATE

But it looks like $this->form_options is never empty and so, method $this->form_options() is never called which in turn never applies the filters siteorigin_widgets_form_options and siteorigin_widgets_form_options_<id_base>

And thus, my chance of modifying the form fields becomes zero with this approach.

The following example may help you: (or rather, it worked well for me)

// Extends the fields for the Button widget.
// @see SiteOrigin_Widget_Button_Widget::get_widget_form()
// @see SiteOrigin_Widget::form_options()
add_filter( 'siteorigin_widgets_form_options_sow-button', function( $form_options, $widget ){
    if ( isset( $form_options['button_icon'] ) ) {
        // `icon_color` is an existing/built-in field for the Button widget. So
        // in this example, we change the `label` from 'Icon color' to 'Icon
        // default color'.
        $form_options['button_icon']['fields']['icon_color']['label'] = 'Icon default color';

        // Or you could completely override the field:
        /*
        $form_options['button_icon']['fields']['icon_color'] = [
            'type'  => 'custom_type_here',
            'label' => 'Icon color',
        ];
        */

        // And here, we add a new field: A color for the button's `hover` state.
        $form_options['button_icon']['fields']['icon_hover_color'] = [
            'type'  => 'color',
            'label' => 'Icon hover color',
        ];
    }

    return $form_options;
}, 10, 2 );

// Callback to handle the `icon_hover_color`.
add_action( 'siteorigin_widgets_after_widget_sow-button', function( $instance, $widget ){
    if ( isset( $instance['button_icon'], $instance['button_icon']['icon_hover_color'] ) ) {
        $selector = $widget->is_preview( $instance ) ?
            '.so-widget-sow-button' : '#' . $widget->id;
        ?>
            <style>
                <?= $selector ?> a:hover .sow-icon-fontawesome {
                    color: <?= $instance['button_icon']['icon_hover_color'] ?> !important;
                }
            </style>
        <?php
    }
}, 10, 2 );

However, even if you set the color fields to other type (e.g. adv_color), you'd still need to create the appropriate PHP class and then add the class name prefix via the siteorigin_widgets_field_class_prefixes hook.

6
random_user_name On

(NOTE: This answer is for the question as originally asked. The question was modified after this answer was posted, so the answer below may not seem like the correct answer to the question in it's current state.)

SiteOrigin Widget Bundle comes with a wide variety of widgets.

Each widget compiles it's own css, utilizing LESS variables.

As you can change the color(s) explicitly for each widget, these variables are stored in the database and retrieved individually for each individual widget and then compiled into widget-specific CSS files. This happens on "Save" of the widget, and the CSS is cached for 7 days.

In order to override the variables for these widgets, you have a few different filters to choose from (refer to the siteorigin-widget.class.php plugin file, the get_instance_css method, starting around line 816 to see the code that does this). FYI you'll want to do it that this level, since this is a "base" class to all of the individual widgets.

Filter 1:

$vars = apply_filters( 'siteorigin_widgets_less_variables_' . $this->id_base, $this->get_less_variables( $instance ), $instance, $this );

This would be an ideal place, because $vars is an array of all the defined colors, and you could easily override any of them that you like. However, the problem is that this filter would be difficult to leverage, since it bakes in the id_base of the widget, which results in filter tags like siteorigin_widgets_less_variables_sow-accordion - however, you could in theory set up a long list of filters, one for each widget, that all run through the same function.

Filter 2:
There's a filter shortly after that one, however the LESS variables have already been "written" into the LESS:

$less = apply_filters( 'siteorigin_widgets_styles', $less, $this->widget_class, $instance );

This is the filter that needs to be used, but you'll need to know the LESS variable names you want to replace. That list (at least some of them) is:

$vars = ‌array (
  'heading_background_color'       => '',
  'heading_background_hover_color' => '',
  'title_color'                    => '',
  'title_hover_color'              => '',
  'heading_border_color'           => '',
  'heading_border_hover_color'     => '',
  'heading_border_width'           => '',
  'has_heading_border_width'       => '',
  'panels_background_color'        => '',
  'panels_font_color'              => '',
  'panels_border_color'            => '',
  'panels_border_width'            => '',
  'has_panels_border_width'        => '',
  'panels_margin_bottom'           => '',
}

Building a Solution:
Armed with this information, we can apply filters like so:

function my_siteorigin_override_less( $less ) {
    // you'll need to load your override colors here.  that is outside the scope of this question / answer
    $my_colors = array(
      'heading_background_color'       => '#fff',
      'heading_background_hover_color' => '#ff0',
      'title_color'                    => '#000'
      // any other colors you want to include / override....
    );

    foreach( $my_colors as $name => $value ) {
        // Ignore empty string, false and null values (but keep '0')
        if( ! empty( $value ) ) {
            $less = preg_replace('/\@'.preg_quote($name).' *\:.*?;/', '@'.$name.': '.$value.';', $less);
        }
    }

    return $less;
}

add_filter( 'siteorigin_widgets_styles', 'my_siteorigin_override_less' );

The above code is tested, and proven to work.

Notes:
SiteOrigin "caches" the CSS it builds for 7 days. That means that if you have widgets already set up, and you change the default color, the changes will not be reflected until the cache is purged somehow.

There is a utility method in the SiteOrigin code that you can use to "purge" the cache when you need to. I would recommend that you do something like below any time the "default" colors are saved / updated:

if ( is_callable( 'SiteOrigin_Widget', 'clear_file_cache' ) ) {
    // pass TRUE to force delete
    SiteOrigin_Widget::clear_file_cache( TRUE );
}