Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I add unyson option to widgets and retrieve their values? #568

Closed
danyj opened this issue May 17, 2015 · 80 comments
Closed

How can I add unyson option to widgets and retrieve their values? #568

danyj opened this issue May 17, 2015 · 80 comments

Comments

@danyj
Copy link
Contributor

danyj commented May 17, 2015

Could you please show me best possible way to add any unsyon option to widgets?
I would like to add list of custom class names that user can pick to display different widgets style/
It is a simple select with list of class names.

This is what I have in mind but with unyson options
http://prntscr.com/76c7vg

@llemurya
Copy link
Contributor

@danyj at the widget_form_callback hook you can run fw()->backend->render_options($options, $values) and at the widget_update_callback you must save your data to the database. Hope this helps.

@danyj
Copy link
Contributor Author

danyj commented May 18, 2015

thnx for the reply but is there any way that you can show it with code sample with one option so I can built on it.

@danyj
Copy link
Contributor Author

danyj commented May 18, 2015

I am having trouble with default value and getting it back

@danyj
Copy link
Contributor Author

danyj commented May 18, 2015

this is what I have

add_action('in_widget_form', 'thz_widget_fields');
add_action('widget_update_callback', 'thz_widget_fields_update_callback');
function thz_widget_fields($instance){

    $settings = $instance->get_settings();

    fw_print($settings);

    $options_array = array();


        $options_array[] = array(

            'widget_style' => array(
                'type'  => 'select',
                'label' => __('Select style','fw'),
                'value' => $settings['widget_style'],
                 'choices' => array(
                     'blue' => __('Blue', 'fw'),
                     'red' => __('Red', 'fw'),
                     'green' => __('Green', 'fw'),

                 ),
            ),
        );  


    echo fw()->backend->render_options($options_array, $data['widget_style'], array(
        'id_prefix' => $option["attr"]["id"] . '-',
        'name_prefix' => $option["attr"]["name"]
    ));


}

function thz_widget_fields_update_callback($instance, $new_instance) {
    $instance['widget_style'] = $new_instance['widget_style'];
    return $instance;
}

but it returns null , I think I missed the field names

http://prntscr.com/76hqyx

@llemurya
Copy link
Contributor

@danyj I am working on make a demo for you please wait. I'll become with an answer.

@llemurya llemurya self-assigned this May 18, 2015
@llemurya
Copy link
Contributor

@danj I make an extension called danj
I created 2 files :
class-fw-extension-danj.php with the next content

<?php if ( ! defined( 'FW' ) ) {
    die( 'Forbidden' );
}

class FW_Extension_Danj extends FW_Extension {

    /**
     * Called after all extensions instances was created
     * @internal
     */
    protected function _init() {
        // TODO: Implement _init() method.
        add_action( 'widget_form_callback', array( $this, 'form_callback' ) );
        add_filter( 'widget_update_callback', array( $this, 'update_callback' ) );
    }

    public function form_callback( $instance ) {
        $options = array(
            'danj' => array(
                'type'  => 'text',
                'value' => 'default value'
            )
        );

        $values = array( 'danj' => isset( $instance['danj'] ) ? $instance['danj'] : $options['danj']['value'] );

        echo fw()->backend->render_options( $options, $values );
    }

    public function update_callback( $instance ) {
        $instance['danj'] = FW_Request::POST( 'fw_options/danj' );

        return $instance;
    }
}

and
manifest.php with the next content

<?php if ( ! defined( 'FW' ) ) {
    die( 'Forbidden' );
}

$manifest = array();

$manifest['name']        = __( 'Danj', 'fw' );
$manifest['description'] = __( 'Danj demo.', 'fw' );
$manifest['version'] = '1.0.0';
$manifest['display'] = true;
$manifest['standalone'] = true;

Here is the video demo how it works. Please try. If it helps, please close the issue.

@danyj
Copy link
Contributor Author

danyj commented May 18, 2015

you over extended yourself and thank you much for it. simple name hint would have been enough but this is much better. than you!

@danyj danyj closed this as completed May 18, 2015
@danyj
Copy link
Contributor Author

danyj commented May 18, 2015

@llemurya should name and id for the options be changed?
if I use the extension as you made it I end up with multiple options and all with same name and ID

49 instances
http://prntscr.com/76jg33

@danyj danyj reopened this May 18, 2015
@llemurya
Copy link
Contributor

Yes if you want you may change it, the code I wrote is a demo and you can change it how you want.

@danyj
Copy link
Contributor Author

danyj commented May 18, 2015

ok , im getting a hang of it. thnx again

@danyj danyj closed this as completed May 18, 2015
@danyj
Copy link
Contributor Author

danyj commented May 18, 2015

@moldcraft @llemurya OK bud , I know you busy that is why I took the task of #542 on my self and it works very well so far .
http://prntscr.com/76m08a

my question is this http://wordpress.stackexchange.com/questions/188648/how-to-get-options-of-all-active-widgets/188651#188651

I have that loop and works fine but it just bugs me to loop that much , but now I found this way to get all widgets options

protected function _init() {
    // TODO: Implement _init() method.
    add_action( 'widget_form_callback', array( $this, 'form_callback' ) );
    add_filter( 'widget_update_callback', array( $this, 'update_callback' ) );
    add_filter( 'dynamic_sidebar_params', array($this, 'widget_params'));

}

public function widget_params( $params ) { 
        //fw_print(  $params  );

        $widget_id      = $params[0]['widget_id'];
        $widget_parts   = explode('-',$widget_id);
        $widget_base_id = $widget_parts[0];
        $widget_number  = $widget_parts[1];         
        $options        = get_option('widget_'.$widget_base_id);
        if($options){
            fw_print(  $options[$widget_number]  );
        }


     return $params;
}

and so far I see it works fine
http://prntscr.com/76lzdt

but do you think it is a problem to use that filter or the way I extracted the widget options?

@danyj
Copy link
Contributor Author

danyj commented May 18, 2015

I am almost done but there is no way that I can fix those id's and names ,
each of them must be unique otherwise the widgets dont display titles and their contents get messed up

the form must use
in_widget_form

instead

widget_form_callback

but when I do that the widget is not updating

we need to use this somehow

 $instance->get_field_name('option_name');
$instance->get_field_id('option_name');

but I cant get it to work

can you please provide a valid example with options names and ids matching the widget instance

with this select

    $options = array(



        'view_type' => array(
            'type'  => 'select',
            'label' => false,
            'value' => 'hide',
             'choices' => array(
                 'hide' => __('Hide on selected pages', 'fw'),
                 'show' => __('Show on selected pages', 'fw'),
             ),
        ),


    );

@danyj danyj reopened this May 18, 2015
@danyj
Copy link
Contributor Author

danyj commented May 19, 2015

please anyone?

@danyj
Copy link
Contributor Author

danyj commented May 19, 2015

unusable to do this as U extension, moved to plugin and everything works out of the box. made simple html options instead unyson options. sad , I wanted to make this unyson extension.
http://prntscr.com/76x50a

thnx for the hellp

@danyj danyj closed this as completed May 19, 2015
@llemurya
Copy link
Contributor

@danyj I made a demo for you please wait 20 minutes to make a video cast.

@danyj
Copy link
Contributor Author

danyj commented May 19, 2015

@llemurya , if you are doing it pay attention to double widget in same widget positon ,

for example the only issue from where everything went down is if you use same widget in same position

for example use text widget twice in same position and you are forced to go native html way because of the widget name and ID

@danyj
Copy link
Contributor Author

danyj commented May 19, 2015

and I also tried with instance->number but you cant hook on it since when widget is firts moved to position that does not exist
http://prntscr.com/76x992

just giving pointers where I had the problems

this one is what we need done right since we have to use it

    echo fw()->backend->render_options($options, $values, array(
        'id_prefix' => $data['id_prefix'] . $id . '-',  // native widget way
        'name_prefix' => $data['name_prefix'] . '[' . $id . ']', // native widget way
    ));

@llemurya
Copy link
Contributor

@danyj please see this videocast If this is what you want I will share the code.

@danyj
Copy link
Contributor Author

danyj commented May 19, 2015

yes , that is the one , all must be unique per each widget as seen in console

@llemurya
Copy link
Contributor

@danyj my demo only works with simple option types that didn't have javascript, with javascript exists problems.

<?php if ( ! defined( 'FW' ) ) {
    die( 'Forbidden' );
}

class FW_Extension_Danj extends FW_Extension {

    /**
     * Called after all extensions instances was created
     * @internal
     */
    protected function _init() {
        // TODO: Implement _init() method.
        add_action( 'widget_form_callback', array( $this, 'form_callback' ) );
        add_filter( 'widget_update_callback', array( $this, 'update_callback' ) );
    }

    public function form_callback( $instance ) {

        $options = array(
            'demo1' => array(
                'type'  => 'text',
                'value' => 'default value'
            ),
            'demo2' => array(
                'type'=>'select',
                'choices' => array(
                    'tiger' => 'Tiger',
                    'puma' => 'Puma',
                    'lynx' => 'Lynx',
                    'pantera' => 'Pantera'
                )
            )
        );

        $values = isset( $instance['fw_options'] ) ? $instance['fw_options'] : array();

        echo fw()->backend->render_options( $options, $values );

        return $instance;
    }

    public function update_callback( $instance ) {

        fw()->backend->option_type('color-picker')->enqueue_static();

        $instance['fw_options'] = FW_Request::POST( 'fw_options' );

        return $instance;
    }
}

@danyj
Copy link
Contributor Author

danyj commented May 19, 2015

testing

@danyj
Copy link
Contributor Author

danyj commented May 19, 2015

nope , same thing , does not work right, title is not updating, give me your email il send you the extension, and test it with text widget , add title and add text , it wont update it

if I used same script in plugin it works perfect but i had to use native widget stuff

@llemurya
Copy link
Contributor

@danyj In the demo screencast the title is updating, please make an screencast how you test it.

@danyj
Copy link
Contributor Author

danyj commented May 19, 2015

here with your code

http://screencast.com/t/gGQwAOWs

protected function _init() {
    // TODO: Implement _init() method.


    add_action( 'widget_form_callback', array( $this, 'in_widget_form' ) );
    add_filter( 'widget_update_callback', array( $this, 'widget_update_callback' ) );
    add_filter('sidebars_widgets', array($this, 'sidebars_widgets'));

}




public function in_widget_form( $instance  ) {



    $options = array(



        'view_type' => array(
            'type'  => 'select',
            'label' => false,
            'value' => 'hide',
             'choices' => array(
                 'hide' => __('Hide on selected pages', 'fw'),
                 'show' => __('Show on selected pages', 'fw'),
             ),
        ),

        'assigned_pages' => array(
            'type'  => 'select-multiple',
            'label' =>false,
            'value' => array(),
            'population' => 'array',
            'choices' => array(
                    'all' =>  __('All pages', 'fw'),

                    array(
                        'attr'    => array('label' => __('Miscellaneous', 'fw')),
                        'choices' => $this->thz_list_miscellaneous(),
                    ),


                    array(
                        'attr'    => array('label' => __('Post types', 'fw')),
                        'choices' => $this->thz_list_post_types(),
                    ),


                    array(
                        'attr'    => array('label' => __('Taxonomy Categories', 'fw')),
                        'choices' => $this->thz_list_taxonomies(),
                    ),  

                    array(
                        'attr'    => array('label' => __('Archives', 'fw')),
                        'choices' => $this->thz_list_archives(),
                    ),                          

                ),


        )
    );

    $values = isset( $instance['fw_options'] ) ? $instance['fw_options'] : array();

    echo fw()->backend->render_options( $options, $values );



}

public function widget_update_callback( $instance ) {

    $instance['fw_options'] = FW_Request::POST( 'fw_options' );

    return $instance;
}

and this is with my plugin no FW

http://screencast.com/t/To3E39j9M

@ghost ghost added the in progress label May 18, 2016
@ghost
Copy link

ghost commented May 18, 2016

/* This CSS need to force display option after saving */
.fw-theme-admin-widget-wrap .fw-backend-option, .fw-theme-admin-widget-wrap .fw-postbox {
    opacity: 1;
}

This css means that you are doing things wrong, option is not displayed because it is not initialized from javascript.


Your code is not using WP_Widget::get_field_name(...); and WP_Widget::get_field_id(...); which generates a unique/incremented name and id, to prevent multiple elements in html with same id/name.

Also you don't initialize the options in javascript (like in the above examples. have you read them?) with $this->print_widget_javascript($id);

Before and After

<?php if ( ! defined( 'ABSPATH' ) ) { die( 'Tranquility - the highest manifestation of power!' ); }

class Widget_Online_Support extends WP_Widget {

    /**
     * Widget constructor.
     */
    private $options;
    private $prefix;
    function __construct() {

        $widget_ops = array( 'description' => __( 'Display online support infomation', 'unyson' ) );
        parent::__construct( false, __( 'Online Support', 'unyson' ), $widget_ops );
        $this->options = array(
            'title' => array(
                'type' => 'text',
                'label' => __('Widget Title', 'unyson'),
            ),
            'block' => array(
                'type'  => 'addable-box',
                'label' => __('Apartment', 'unyson'),
                'box-options' => array(
                    'skype' => array( 'type' => 'text', 'label' => __('Skype', 'unyson'), ),
                    'tel' => array( 'type' => 'text', 'label' => __('Telephone', 'unyson'), ),
                    'desc' => array( 'type' => 'text', 'label' => __('Aparment name', 'unyson'), ),
                ),
                'box-controls' => array( // buttons next to (x) remove box button
                    'control-id' => '<small class="dashicons dashicons-smiley"></small>',
                ),
                'limit' => 0, // limit the number of boxes that can be added
                'add-button-text' => __('Add New', 'unyson'),
                'sortable' => true,
            ),

        );
        $this->prefix = 'online_support';
    }

    function widget( $args, $instance ) {
        extract( $args );
        $params = array();

        foreach ( $instance as $key => $value ) {
            $params[ $key ] = $value;
        }

        $filepath = dirname( __FILE__ ) . '/views/widget.php';

        $instance = $params;

        if ( file_exists( $filepath ) ) {
            include( $filepath );
        }
    }

    function update( $new_instance, $old_instance ) {
        return fw_get_options_values_from_input(
            $this->options,
            FW_Request::POST(fw_html_attr_name_to_array_multi_key($this->get_field_name($this->prefix)), array())
        );
    }

    function form( $values ) {

        $prefix = $this->get_field_id($this->prefix);
        $id = 'fw-widget-options-'. $prefix;

        echo '<div class="fw-force-xs fw-theme-admin-widget-wrap" id="'. esc_attr($id) .'">';
        $this->print_widget_javascript($id);
        echo fw()->backend->render_options($this->options, $values, array(
            'id_prefix' => $prefix .'-',
            'name_prefix' => $this->get_field_name($this->prefix),
        ));
        echo '</div>';

        return $values;
    }

    private function print_widget_javascript($id) {
        ?><script type="text/javascript">
            jQuery(function($) {
                var selector = '#<?php echo esc_js($id) ?>', timeoutId;

                $(selector).on('remove', function(){ // ReInit options on html replace (on widget Save)
                    clearTimeout(timeoutId);
                    timeoutId = setTimeout(function(){ // wait a few milliseconds for html replace to finish
                        fwEvents.trigger('fw:options:init', { $elements: $(selector) });
                    }, 100);
                });
            });
        </script><?php
    }

}

@ghost ghost closed this as completed May 18, 2016
@ghost ghost removed the in progress label May 18, 2016
@dinhtungdu
Copy link

dinhtungdu commented May 18, 2016

@moldcraft You're my hero! Worked perfectly! Next time, I will check my code more carefully before submit here. Thanks for pointing my mistakes!

@pear1
Copy link

pear1 commented Jun 7, 2016

Hi there,

Just tested @moldcraft script and everything works very well except JavaScript is no initialized when widget is added and page is not reloaded.
When I reload page, there are no issues and everything works.

I can't see any JavaScript or PHP errors/warnings.

EDIT: When pressing Save button twice it starts working (after first click options simply disappears, but after second click it loads again and JS starts working)

@dinhtungdu
Copy link

@pear1 In my updated version, when user add new widget, he just needs one click on Save button. This causes by the not matching id in javascript of that widget when it's added first time. I'm trying to make it work at the first time but at the moment no luck.

@pear1
Copy link

pear1 commented Jun 8, 2016

@dinhtungdu At the moment only solution I can think of would be to trigger Save button click twice by JS, immediately after widget is added. Then content is fully reloaded and JS starts working.
That could be a pretty bad solution, but at least everything would work as it should.

@noesteijver
Copy link

Does this have anything to do with the following problem?

http://blog.codebusters.pl/en/click-doesn-t-work-after-ajax-load-jquery/

@danyj
Copy link
Contributor Author

danyj commented Sep 10, 2016

@dinhtungdu I tested your latest gist just now and seems like is not working anymore ,

@moldcraft what is the 100% working code now since we mixed up extension that adds option and widgets with options

@danyj
Copy link
Contributor Author

danyj commented Sep 10, 2016

@moldcraft , @dinhtungdu , @Priler hm , we saved everything and all nice and sweet , but does any of you know how to retrieve the saved values per widget?

@ghost
Copy link

ghost commented Sep 10, 2016

what is the 100% working code now since we mixed up extension that adds option and widgets with options

I will inspect this issue later.

we saved everything and all nice and sweet , but does any of you know how to retrieve the saved values per widget?

They should be somewhere in wp_options

Usually a widget is like a shortcode, the $atts are available only on render, in widget case it is in widget() method https://codex.wordpress.org/Widgets_API

@ghost ghost reopened this Sep 10, 2016
@ghost ghost added ready and removed ready labels Sep 10, 2016
@danyj danyj closed this as completed Sep 30, 2016
@toleabivol
Copy link

toleabivol commented Nov 5, 2016

This fixes the disappearing options in @dinhtungdu example.

jQuery(function($) {
    var timeoutId;
    $(document).on('widget-updated widget-added', function(){
        clearTimeout(timeoutId);
        timeoutId = setTimeout(function(){ // wait a few milliseconds for html replace to finish
            fwEvents.trigger('fw:options:init', { $elements: $('#widgets-right .fw-theme-admin-widget-wrap') });
                    }, 100);
        });
    });

Note: you should put this in a script file and load it once. Still it's not a clean script but it does it's job and no visual side effects for user. Waiting for an official fix though.

@danyj
Copy link
Contributor Author

danyj commented Mar 2, 2017

@moldcraft , I tried something else and seems to work , but need small help ,

most of us would have bunch of shortcodes that we want to have as widgets also , so instead of copy the code I did this

function __construct() {

    $widget_ops 	= array( 'description' => __( 'Display online support infomation', 'unyson' ) );
    parent::__construct( false, __( 'Tabs', 'unyson' ), $widget_ops );
	
	$sh_options   = fw_get_variables_from_file(get_template_directory() .$this->_sh_path('tabs').'/options.php', array('options' => null));
	$sh_options   = $sh_options['options'];
	
    $this->options = array(
       
		'widget_options' => array(
			'type' => 'popup',
			'options' =>  $sh_options,
			'title' => __('Widget Options', '{domain}'),
			'modal-size' => 'large',
			'desc' => __('Customize Widget Options', '{domain}'),
		),			

    );
	
    $this->prefix = 'online_support';
}


function _sh_path($shortcode){
	
	return '/inc/thzframework/extensions/shortcodes/shortcodes/'.$shortcode;
}	


function widget( $args, $instance ) {
    extract( $args );
    $params = array();

    foreach ( $instance as $key => $value ) {
        $params[ $key ] = $value;
    }

    $filepath = get_template_directory() .$this->_sh_path('tabs').'/views/view.php';

    $instance = $params;

    if ( file_exists( $filepath ) ) {
        include( $filepath );
    }
}

and in tabs shortcode just on top of view.php

if(isset($instance)){
	$atts = $instance;
}

now I have full tabs output in front and options in widget ,

but 2 things are bugging

  1. missing the static.php from shortcode in frontent with dynamic css
  2. the popup container hides tabs after the widget has been saved the first time

http://prntscr.com/efbmih

it would be great if you could help.

@danyj danyj reopened this Mar 2, 2017
@danyj danyj unassigned ghost Mar 2, 2017
@danyj
Copy link
Contributor Author

danyj commented Mar 2, 2017

as of popup container , I can move that to popup option type and do this in view

if(isset($instance)){
	$atts = $instance['widget_options'];
}

but still need the static

@danyj danyj closed this as completed Mar 17, 2017
@toleabivol
Copy link

toleabivol commented Apr 6, 2017

This is my final solution to the IN WIDGET options
using @moldcraft example above , add in a single enqueued to widgets page script :

/* 
	Enable FW options in widgets on add (without refresh)
	By simulating a save click after add and then initialize them with fw:options:init
 */
jQuery(function($) {
	let timeoutAddId;
	$(document).on('widget-added', function(ev, $widget){
		clearTimeout(timeoutAddId);
		timeoutAddId = setTimeout(function(){ // wait a few milliseconds for html replace to finish
			$widget.find('form input[type="submit"]').click();
		}, 300);
	});

	let timeoutUpdateId;
	$(document).on('widget-updated', function(ev, $widget){
		clearTimeout(timeoutUpdateId);
		timeoutUpdateId = setTimeout(function(){ // wait a few milliseconds for html replace to finish
			fwEvents.trigger('fw:options:init', { $elements: $widget });
		}, 100);
	});
});

You get the $widget object as the second parameter for events widget-added and widget-updated so no need to search for that.

@mhsohag11
Copy link

Thanks @moldcraft , But i face another problem. When i add "icon-v2" field in widget area, in popup option no field are shown !!!
How can i solve it ???

@saddamwp
Copy link

Hello, mhsohag11, you are a genius person. now, you are working for GitHub unison group. I didn't understand why you do not solve this. I hope you can try, you will be solved this problem.because you are a genius person.

@yura-x
Copy link
Contributor

yura-x commented May 18, 2018

Be careful if you are using 'wp-editor' option type in your widget since WordPress version 4.9.6.
You'll see error in your console.
More info on WordPress trac
It can be fixed in includes/option-types/wp-editor/static/scripts.js by wrapping code tinymce.ui.FloatPanel.zIndex = 100100; on line 148 in if statement like so:
if ( tinymce.ui.FloatPanel !== undefined ) { tinymce.ui.FloatPanel.zIndex = 100100; }
Please note that you'll loose this changes after Unyson plugin update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants