0

Big discovery, check edit 3, way down.

What I'm trying to do, in short: Based on an AJAX call (which is tied to an user clicking), I'm installing a plugin. This button that tells my PHP to "install this plugin" is on a custom page that I've made with add_theme_page.

I've registered this function inside my class that helps me with an AJAX request:

public function __construct()
{
    add_action( 'wp_ajax_parse_plugin_request', array( $this, 'parsePluginInstallRequest' ) );
}

The function being:

public function parsePluginInstallRequest()
{
    check_ajax_referer( 'plugin_routines', 'security' );
    $data = json_decode( stripslashes( $_POST['plugin_install_request_data'] ), true );
    //$data is what comes when an user clicks a button, just a handle for the plugin that needs to be installed.
    $this->installPlugin( $data['plugin-slug'] );
}

And so, it calls installPlugin which is just a wrapper for a quick and dirty "install plugin". The thing is, that line of code:

$this->installPlugin( $data['plugin-slug'] );

Works just fine out of this call, even tested with a wp_send_json inside of installPlugin to see whether or not it was being called and it's being called.

My JS script that calls everything: //This is being called in the back-end, on a custom page. function requestPluginAction(plugin_data) {

    var ajaxsecurity = setup_page_params.plugins_routines_nonce

    jQuery.ajax(
        {
            url: ajaxurl,
            type: 'POST',
            dataType: 'json',
            data: {
                action: 'parse_plugin_request',
                security: ajaxsecurity,
                plugin_install_request_data: plugin_data
            },

            success: function (response) {
                console.log(response);
            },

            error: function (error) {
                console.log(error);
            }
        }
    );
}

I have checked multiple times that, indeed, the data is getting passed correctly, so, PHP sees what it needs to see in order to work.

But to no avail.

A few notes: I am extending the WP_Upgrader minimally, just making it's WP_Upgrader_Skin not output anything, but rather, return data.

I've put debugging messages everywhere in these classes, output buffering, breakpoints and what not, yet I still can't understand why it does this.

In the back-end, I could install plugins lightning fast, 3-4 at once, but when the call to installPlugin comes from AJAX, it doesn't do anything. At all.

My one and only idea is that when I'm inside my wp_ajax hook'd function, parsePluginInstallRequest, it simply is too early to call the install, put it this way:

I'm trying to install a plugin when the plugins haven't loaded / other things haven't loaded, basically this parsePluginInstallRequest is hooked to: wp_ajax_parse_plugin_request, so I am calling the install of a plugin on this hook.

It might be too early and my installer fails horribly because WordPress is not done yet.

But then, how can I fire this event for installing on user-click, when the install doesn't work at that point in time? It's a contradiction.

Edit 1:

It seems that if I output anything (with var_dump) before the main content of my custom page, the AJAX calls no longer work at all, so putting a simple echo or wp_send_json within my parsePluginInstallRequest no longer works and it returns all kinds of things.

Edit 2:

I'm getting my list of plugins using TGMPA, I just checked and the array of plugins that is 100% populated, when being called from an AJAX context is empty.

So, if I call installPlugins from outside of the wp_ajax-$hook_here context, it registers the array and it all works, but if I do it from the inside, it doesn't see the array coming from TGMPA.

Leads me to think that I'm firing my AJAX way too soon and it doesn't catch TGMA's populating of the array I need to use.

Edit 3: Big stuff. After 8 exhausting hours, I finally got to the core. I'm drawing my array from TGMPA. Thing is, TGMPA fires on init, now, remember, I'm on a page added by add_theme_page and I'm firing my AJAX on this page. My call to installPlugins is registered to also go off when the user clicks my button, so on wp_ajax_parse_plugin_request.

Thing is - my installPlugins which parses TGMPA's array, like this:

    foreach( $plugins['install'] as $plugin_key => $plugin_data ) {
        $this->upgrader->install( $plugin_data['source'] );

Will always have $plugins empty, because TGMPA hasn't yet populated the plugin, since we're not hitting init as of yet.

So what I did was, go on ahead, copy what TGMPA's array actually is and hard-code that array into the installPlugins function and voila - it works.

So, the real question is: how do I make my AJAX hook after init, so that $plugins can be populated?

3
  • Can you paste here how are you calling it from your js? Jun 8, 2018 at 1:42
  • @FelipeElia Sure. Any help is appreciated, I've already lost 5 hours just on this.
    – coolpasta
    Jun 8, 2018 at 2:18
  • @FelipeElia Just added more details.
    – coolpasta
    Jun 8, 2018 at 2:28

1 Answer 1

0

My one and only idea is that when I'm inside my wp_ajax hook'd function, parsePluginInstallRequest, it simply is too early to call the install, put it this way:

I don't think so, when the wp_ajax hook is called, your class and methods are loaded. That can't be it.

Your ajax looks good, but i always use this approach: (setting nonce is easier)

<?php
add_action( 'admin_footer', 'my_action_javascript' );
function my_action_javascript() {
  //Set Your Nonce
  $ajax_nonce = wp_create_nonce( "my-special-string" );
  ?>
  <script>
  jQuery( document ).ready( function( $ ) {
    var data = {
      action: 'my_action',
      security: '<?php echo $ajax_nonce; ?>',
      whatever: 1234
    };
    // Since 2.8 ajaxurl is always defined in the admin header and points to admin-ajax.php
    $.post( ajaxurl, data, function( response ) {
      alert( 'Got this from the server: ' + response );
    });
  });
  </script>
<?php } ?>

Is the debug log telling you anything?


$data = json_decode( stripslashes( $_POST['plugin_install_request_data'] ), true );

I guess you debugged this, but just to be sure, are you certain the $data array is correct after json_decode()?

If the array key is not found, it will result in an error and the function will not run, but this would be visible in the debug log...

EDIT:

Ok, you mention:

TGMPA fires on init.

I'm not that familiar with the TGMPA source code (i know what it does), but if it has the following (or something like this) it will never work:

if( ! wp_doing_ajax() ) {}

Did you check?

I've called many functions from wp_ajax that are only available after the init hook.

p.s. after a (quick) search i cannot find any conclusive info when the wp_ajax hook runs :S..

3
  • I fixed it with a static array. It seems that TGMPA (the generator of my array) just doesn't fire at the moment I'm calling my function. Their class initializes on init, while I'm way in the back-end, I'm just not catching it. I'm researching right now when exactly wp_ajax-$name fires, because it's definitely before init, so I don't get any data.
    – coolpasta
    Jun 8, 2018 at 5:19
  • Check my latest edit.
    – coolpasta
    Jun 8, 2018 at 5:24
  • see my edit....
    – Bjorn
    Jun 8, 2018 at 5:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.