michaeluno / admin-page-framework

Facilitates WordPress plugin and theme development.
http://admin-page-framework.michaeluno.jp/
Other
337 stars 71 forks source link

Enqueue inline style from a widget #212

Closed vladkucherov closed 9 years ago

vladkucherov commented 9 years ago

Hi,

I have a widget that create style settings to each instance of the widget. At first I tried to create the <style> tag right after the content of the widget, it worked but the Validator didn't like it.

So I moved the code the the footer using wp_footer action, but that didn't help the Validator After a brief look up, I found that it must be in the head starting from HTML5 (this was something new to me)...

Is there a way to enqueue styles from a widget with the $aForm settings? __construct and setUp and load methods are not an option as they are not giving me the value of $aForm

Any ideas?

michaeluno commented 9 years ago

Try the start() method. That is the user constructor.

vladkucherov commented 9 years ago

@michaeluno I don't think I understood you.

I tried to add:

public function start($sContent, $aArguments, $aFormData) {
    var_dump($sContent, $aArguments, $aFormData);
}

But I got: Strict standards: Declaration of TopFive_Dynamic_List_Widget::start() should be compatible with AdminPageFramework_Factory_Controller::start() Missing argument 1 for TopFive_Dynamic_List_Widget::start() X 3 Undefined variable: ... X 3

Which all basically means that start() is not getting any parameters.

I can you explain what you meant?

michaeluno commented 9 years ago

Hi,

I missed the part you want to access the $oForm object property. In that case just access it like $this->oForm. (*corrected)

To be clear the start() method does not receive any. If you want to check passed parameters, you may want to use func_get_args().

Hope it helps!

vladkucherov commented 9 years ago

@michaeluno Actually I need not $oForm, I need $aFormData like in content() method. Why? I need to enqueue styles to the head.

Because it should work from the head, I can't use content() as it's called after the head.

$this->oForm doesn't contain the form data.

Any other ideas?

michaeluno commented 9 years ago

I'm not sure if I understand your question but maybe in the start() method, you may want to retrieve the widget options from the options table.

    public function start() {
        $_aWidgetsFormData = get_option( 
            // widget_{id -base}
            'widget_' . strtolower( get_class( $this ) ),
            array()
        );
        // Do your stuff with the data.
        $this->oDebug->log( $_aWidgetsFormData );
    }

Just you would need to keep in mind that the retrieved array will hold multiple sets of widget form data as the user can create multiple instances of widgets from the same widget model.

vladkucherov commented 9 years ago

@michaeluno How can I get the current instance?

michaeluno commented 9 years ago

What do you mean by current instance?

vladkucherov commented 9 years ago

image

It returned 3 widgets as expected, but they are all the same.

michaeluno commented 9 years ago

So they are the current ones, aren't they? I do not get what you are looking for.

vladkucherov commented 9 years ago

I'll explain the situation from the beginning.

The widget, depending on the settings, creates some CSS stylesheet. To pass the validator, I should move the stylesheet to the head. To move the to the head, I should use other methods than content() as it's called after wp_head action is fired.

The possible solution would be like you suggested is to use the start() method with the code you suggested to fetch the widgets, but it fetches the same widget with the wrong data as much times as I have the widget on the page - this is bad.

michaeluno commented 9 years ago

The widget, depending on the settings, creates some CSS stylesheet.

In the front-end?

but it fetches the same widget with the wrong data as much times as I have the widget on the page

Which page, widgets.php in the admin or a front-end page that the widget is displayed?

vladkucherov commented 9 years ago

In the front-end?

Let's simplify, I need to create a string, using the data set in the widget form (via admin) that will go the the head (this string is inline CSS)

Which page, widgets.php in the admin or a front-end page that the widget is displayed?

I was talking about the solution you provided:

public function start() {
    $_aWidgetsFormData = get_option( 
        // widget_{id -base}
        'widget_' . strtolower( get_class( $this ) ),
        array()
    );
    // Do your stuff with the data.
    $this->oDebug->log( $_aWidgetsFormData );
}

The widgets are being used not from a sidebar but from siteorigin_panels

michaeluno commented 9 years ago

Hi,

I'm still unable to fully comprehend your problem and question.

Let's simplify, I need to create a string, using the data set in the widget form (via admin) that will go the the head (this string is inline CSS)

Are you trying to generate a string in front-end pages where most visitors can view or in the admin area where only members can access?

vladkucherov commented 9 years ago

Hi,

I am trying to let the users in the admin area to be able to add CSS. They have ACE editor where they can write it.

I need the CSS they wrote to use in the HEAD of the document instead of the body (Validators strict HTML5 Standards)

Hope this clarifies the situation.

michaeluno commented 9 years ago

Okay, with the method I provided above, you said you get the wrong stored form data and they are not the current ones.

I guess it is because the data are retrieved only once when the page is rendered. However, the user can update the widget form many times in a single page load with JavaScript. So I think you should do what you are trying all in JavaScript (jQuery) by hooking the widget update event and inserting CSS into the <head> tag.

vladkucherov commented 9 years ago

I think it's a bad idea. What if the JS is disabled? the user will get very bad looking website :cry:

michaeluno commented 9 years ago

If the user disables JavaScript in the browser, the user won't be able to dynamically save the widget form anyway. In that case, you can rely on the method introduced above.

I'm going to close this topic as it is not of the framework.

vladkucherov commented 9 years ago

@michaeluno I need the data from the site and not from the admin. I think I haven't made myself clear.

The administrator should enter the data from the admin using a field. the data is made with multiple fields and one of them is a field where the administrator should write CSS (here I don't expect the JS to be turned off).

After the administrator saves the widget - the widget should render the result based on the data. like I said before, there should be CSS.

I need to put the CSS in the head of the document in the website (not admin... who cares if the admin passes W3C...). The content() method executes after wp_head action, so I can't assign the CSS to the head as you suggested, I may use start() method but that has only the last data for each instance of the widget on the page (like it's duplicated).

michaeluno commented 9 years ago

I need the data from the site and not from the admin

That's what you need to be clear from the beginning.

I am trying to let the users in the admin area to be able to add CSS.

You wrote this on the other hand in one of your replies so what you say is confusing.


Anyway, let's assume you want to generate a style-sheet in front-end pages from previously stored widget form data set in the admin area.

As far as I know, basically there is no means to know which widget is going to be displayed at the time WordPress renders the <hread> tag. It won't be known until the sidebar containing the widget gets rendered. And it is determined by the theme.

A workaround I suggest:

  1. Embed a widget instance id with a hidden field. (You need the value later when you output the widget in the front-end.)

       $_oWidget = $GLOBALS[ 'wp_widget_factory' ]->widgets[ $this->oProp->sClassName ];
       $this->addSettingFields(
           ... your other fields here...
           array(
               'field_id'  => '_widget_instance_id',
               'type'      => 'hidden',
               'value'     => $_oWidget->id,
           )
       );          
  2. Let the user save their CSS in the widget form. Let's say your user saves the following CSS in the widget form of a widget instance of 2.

    .my-css {
    padding: 1em;
    }
  3. Parse the saved form data in the start() method and generate style-sheets of all of the widget instancees (because we won't know which one will be rendered explained above). Each widget form data should have the widget instance ID set in the above embedded hidden field.

    Prepend a class selector of the widget instance id to the user defined custom CSS in your generated stylesheet like the below and enqueue it in the <head> tag.

    .your-widget-inscance-id-2 .my-css {
       padding: 1em;
    }
    .your-widget-inscance-id-2 .your-user-set-css {
       margin: 1em;
    }
    .your-widget-inscance-id-4 .another-instance {
       float: right;
    }
  4. In the content() method, make sure to enclose the output with the class selector of the widget instance id.

    public function content( $sContent, $aArguments, $aFormData ) {
       $_sWidgetInstanceID = $aFormData[ '_widget_instance_id' ]; 
       return "<div class='{$_sWidgetInstanceID}'>" 
               . $this->getYourWidgetOutput( $sContent, $aArguments, $aFormData  )
           . "</div>";
    }    
vladkucherov commented 9 years ago

@michaeluno That would be great but when I try to get the widgets data from start() method using what you have suggested before:

    public function start() {
        $_aWidgetsFormData = get_option( 
            // widget_{id -base}
            'widget_' . strtolower( get_class( $this ) ),
            array()
        );
        // Do your stuff with the data.
        $this->oDebug->log( $_aWidgetsFormData );
    }

Won't return all the properties as expected - it'll return the last instance duplicated by the times the widget was used.

This basically was the initial issue (I need to learn how to express my self apparently :smile: )

michaeluno commented 9 years ago

Won't return all the properties as expected - it'll return the last instance duplicated by the times the widget was used.

Not sure what you are expecting. Post the output of the retrieved widget form data.

vladkucherov commented 9 years ago

I posted it in an image image

As you can see there are 3 widgets, each of them has multiple parameters - but they are all the same. I don't have 3 widgets with the same data in each of them. each widget is different, but in the image above you can see that it's stored with the same (actually it's data of the last widget - looks like it's overwriting the rest)

michaeluno commented 9 years ago

Why are you posting the same image you posted before again? It misses the array index which is important as it indicates the instance number.

Try what is suggested. Include a hidden field that embeds the widget instance id, which will be a huge clue on finding out what is going on in your environment. Then post the results.