DevinVinson / WordPress-Plugin-Boilerplate

[WordPress] A foundation for WordPress Plugin Development that aims to provide a clear and consistent guide for building your plugins.
http://wppb.io
7.67k stars 2.25k forks source link

Using the plugins_api filter. #477

Open multiplehats opened 6 years ago

multiplehats commented 6 years ago

Hey guys!

I feel I'm missing something incredibly obvious. I'm trying to use the plugins_api filter to show the View Details link on the plugins.php page. The function is working correctly for all plugins except mine. I feel it has something to do with how I register the filter.

Here's is my function. When I remove if( $this->plugin_name !== $args->slug ) I can see the View Details on other plugins except mine. I checked the $args->slug and $this->plugin_name and they are identical.

    public function view_details( $res, $actions, $args ){

      $action = 'plugin_information';

        // do nothing if this is not about getting plugin information
        if( $action !== 'plugin_information' )
            return false;

        //do nothing if it is not our plugin
        if( $this->plugin_name !== $args->slug )
            return false;

        // trying to get from cache first
        if( false == $remote = get_transient( 'xxx' ) ) {

            // info.json is the file with the actual plugin information on the server
            $remote = wp_remote_get( 'http://xxx.local/info.json', array(
                'timeout' => 10,
                'headers' => array(
                    'Accept' => 'application/json'
                ) )
            );

            if ( !is_wp_error( $remote ) && isset( $remote['response']['code'] ) && $remote['response']['code'] == 200 && !empty( $remote['body'] ) ) {
                set_transient( 'xxx, $remote, 43200 ); // 12 hours cache
            }

        }

        if( $remote ) {

            $remote = json_decode( $remote['body'] );
            $res = new stdClass();
            $res->name = $remote->name;
            $res->slug = $this->plugin_name;
            $res->version = $remote->version;
            $res->tested = $remote->tested;
                $res->tested = $remote->homepage;
            $res->requires = $remote->requires;
            $res->author = '<a href="https:/xxx.com">Chris Schwartze</a>';
            $res->author_profile = 'https://profiles.wordpress.org/xxx';
            $res->download_link = $remote->download_url;
            $res->trunk = $remote->download_url;
            $res->last_updated = $remote->last_updated;
            $res->sections = array(
                'changelog' => $remote->sections->changelog
            );

            if( !empty( $remote->sections->screenshots ) ) {
                $res->sections['screenshots'] = $remote->sections->screenshots;
            }
                return $res;
        }
        return false;
    }

Here's my class-plugin-name.php

   /**
     * Register all of the hooks related to the admin area functionality
     * of the plugin.
     *
     * @since    1.0.0
     * @access   private
     */
    private function define_admin_hooks() {
      $this->loader->add_filter('plugins_api' , $plugin_admin, 'view_details', 10, 3);
    }

Any pointers would be really appreciated!

dingo-d commented 6 years ago

What is $plugin_admin in the define_admin_hooks() method? :)

multiplehats commented 6 years ago

Thanks for the quick reply @dingo-d

$plugin_admin = new My_Plugin_Name_Admin($this->get_plugin_name(), $this->get_version());

dingo-d commented 6 years ago

Did you put it inside the method? Because you'll either need to add it inside the method to make this variable available inside the method, or maybe add it in the constructor like $this->plugin_admin, and then use it as such inside the method

$this->loader->add_filter('plugins_api' , $this->plugin_admin, 'view_details', 10, 3);
multiplehats commented 6 years ago

@dingo-d Apologies, maybe I wasn't clear enough with my example.

I believe $this->plugin_name' is setup inpublic function __construct()' out of the box. At least, I haven't made much changes to that function. Here it is in full:

    public function __construct()
    {
        if (defined('PLUGIN_NAME_VERSION')) {
            $this->version = PLUGIN_NAME_VERSION;
        } else {
            $this->version = '1.0.0';
        }
        $this->plugin_name = 'juggle-for-woocommerce';
        $this->load_dependencies();
        $this->set_locale();
        $this->define_admin_hooks();
        $this->define_public_hooks();
    }
dingo-d commented 6 years ago

Ok, I wanted you to do this yourself (learning experience) but I'll help :D

Either do this:

 public function __construct()
    {
        if (defined('PLUGIN_NAME_VERSION')) {
            $this->version = PLUGIN_NAME_VERSION;
        } else {
            $this->version = '1.0.0';
        }
        $this->plugin_name = 'juggle-for-woocommerce';
        $this->load_dependencies();
        $this->set_locale();
        $this->define_admin_hooks();
        $this->define_public_hooks();
        $this->plugin_admin = new My_Plugin_Name_Admin( $this->plugin_name, $this->version );
    }

   /**
     * Register all of the hooks related to the admin area functionality
     * of the plugin.
     *
     * @since    1.0.0
     * @access   private
     */
    private function define_admin_hooks() {
      $this->loader->add_filter('plugins_api' , $this->plugin_admin, 'view_details', 10, 3);
    }

or do this:

   /**
     * Register all of the hooks related to the admin area functionality
     * of the plugin.
     *
     * @since    1.0.0
     * @access   private
     */
    private function define_admin_hooks() {
      $plugin_admin = new My_Plugin_Name_Admin($this->get_plugin_name(), $this->get_version());

      $this->loader->add_filter('plugins_api' , $plugin_admin, 'view_details', 10, 3);
    }

I'm usually doing the second way, even though it's not perfect.

Hope this helps :)

multiplehats commented 6 years ago

@dingo-d Appreciate that! I actually have $plugin_admin = new My_Plugin_Name_Admin($this->get_plugin_name(), $this->get_version()); in my private function define_admin_hooks(), I didn't mention it at first because I assumed it was common practice, since it's included in the boilerplate: https://github.com/DevinVinson/WordPress-Plugin-Boilerplate/blob/60940a97614170845242849d0b78e6def0d06073/plugin-name/includes/class-plugin-name.php#L155

My bad... any other ideas?

dingo-d commented 6 years ago

Is your plugin hosted on wordpress.org?

multiplehats commented 6 years ago

@dingo-d No it isn't, but I know it's possible to override the plugin details. I'm trying to do this basically: https://rudrastyh.com/wordpress/self-hosted-plugin-update.html

dingo-d commented 6 years ago

Ok, then you need to turn on the debugging, and put a bunch of error_log()s around and see where your method breaks. See if it's called at all, if not, why.

multiplehats commented 6 years ago

@dingo-d Yeah, I've been digging for a while—hence why I'm here. it's being called, everywhere except on my plugin. Last resort is probably Stackoverflow ;)

multiplehats commented 6 years ago

So after some more debugging, I can see it's working fine except on my own plugin instance. Heck another function that hooks into site_transient_update_plugin is working and I can see WordPress showing an update notification (the red icon in the menu) but not below my plugin. I feel this has to do with how it loads in the plugin boilerplate as I just tried it on a vanilla plugin and it worked fine.

If anyone ever tried to set up a View Details link on the plugins.php page let me know. I'd love to figure this one out.

dingo-d commented 6 years ago

I just pasted the code and got an error

set_transient( 'xxx, $remote, 43200 ); // 12 hours cache

You're missing a ' in xxx :)

set_transient( 'xxx', $remote, 43200 ); // 12 hours cache
dingo-d commented 6 years ago

But besides that, the filter won't fire. It did fire when I viewed 'view details' of another plugin when I tested this.

Try asking on wordpress.stackowerflow :/

multiplehats commented 6 years ago

@dingo-d thanks for sticking with me, I appreciate it.

I was probably missing an ' because I removed some code when I was editing my first post. I just checked my code, and it's there.

But exactly, it only fires when you view 'view details' of another plugin... 👎

i guess i'll reside to SO.

1jump2 commented 2 years ago

I've worked it out as I was facing the exact same problem with Boilerplate and the referenced plugins_api code. The issue is in the Update function where the plugin filename is set. It's set using plugin_basename( FILE ) which actually returns 'plugin/admin/class-plugin-admin.php' but you want it to return plugin/plugin.php. Hardcode that (replacing it with the name of your plugin) and it'll work.

connectwithAbhishek commented 2 years ago

@1jump2 Thank you Sir, it works :)

wolffe commented 1 year ago

@1jump2 Yes, it works!

The example had the plugin_basename( __FILE__ ) because it was the main plugin file. In my case, and probably yours and the OP's, the updater was in a different file. Hardcoding the plugin slug fixed the issue.